diff --git a/src/plugins/home/tsconfig.json b/src/plugins/home/tsconfig.json new file mode 100644 index 00000000000000..b2613eeecdfb0d --- /dev/null +++ b/src/plugins/home/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "config.ts", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../data/tsconfig.json" }, + { "path": "../kibana_react/tsconfig.json" }, + { "path": "../url_forwarding/tsconfig.json" }, + { "path": "../usage_collection/tsconfig.json" }, + { "path": "../telemetry/tsconfig.json" }, + ] +} diff --git a/src/plugins/saved_objects_tagging_oss/tsconfig.json b/src/plugins/saved_objects_tagging_oss/tsconfig.json new file mode 100644 index 00000000000000..b0059c71424bf5 --- /dev/null +++ b/src/plugins/saved_objects_tagging_oss/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "common/**/*", + "public/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../saved_objects/tsconfig.json" }, + ] +} diff --git a/test/functional/page_objects/dashboard_page.ts b/test/functional/page_objects/dashboard_page.ts index 8924d22cdb50f6..cc1420e4825c26 100644 --- a/test/functional/page_objects/dashboard_page.ts +++ b/test/functional/page_objects/dashboard_page.ts @@ -33,6 +33,7 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide const dashboardAddPanel = getService('dashboardAddPanel'); const renderable = getService('renderable'); const listingTable = getService('listingTable'); + const elasticChart = getService('elasticChart'); const PageObjects = getPageObjects(['common', 'header', 'visualize']); interface SaveDashboardOptions { @@ -275,6 +276,20 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide } } + public async isColorSyncOn() { + log.debug('isColorSyncOn'); + await this.openOptions(); + return await testSubjects.getAttribute('dashboardSyncColorsCheckbox', 'checked'); + } + + public async useColorSync(on = true) { + await this.openOptions(); + const isColorSyncOn = await this.isColorSyncOn(); + if (isColorSyncOn !== 'on') { + return await testSubjects.click('dashboardSyncColorsCheckbox'); + } + } + public async gotoDashboardEditMode(dashboardName: string) { await this.loadSavedDashboard(dashboardName); await this.switchToEditMode(); @@ -554,6 +569,10 @@ export function DashboardPageProvider({ getService, getPageObjects }: FtrProvide return 0; } } + + public async getPanelChartDebugState(panelIndex: number) { + return await elasticChart.getChartDebugData(undefined, panelIndex); + } } return new DashboardPage(); diff --git a/test/functional/services/visualizations/elastic_chart.ts b/test/functional/services/visualizations/elastic_chart.ts index 1f1f7df45f4605..86ca4d1c1e31e8 100644 --- a/test/functional/services/visualizations/elastic_chart.ts +++ b/test/functional/services/visualizations/elastic_chart.ts @@ -81,19 +81,23 @@ export function ElasticChartProvider({ getService }: FtrProviderContext) { } } - private async getChart(dataTestSubj?: string, timeout?: number): Promise { + private async getChart( + dataTestSubj?: string, + timeout?: number, + match: number = 0 + ): Promise { if (dataTestSubj) { if (!(await testSubjects.exists(dataTestSubj, { timeout }))) { throw Error(`Failed to find an elastic-chart with testSubject '${dataTestSubj}'`); } - return await testSubjects.find(dataTestSubj); + return (await testSubjects.findAll(dataTestSubj))[match]; } else { const charts = await this.getAllCharts(timeout); if (charts.length === 0) { throw Error(`Failed to find any elastic-charts on the page`); } else { - return charts[0]; + return charts[match]; } } } @@ -106,8 +110,11 @@ export function ElasticChartProvider({ getService }: FtrProviderContext) { * used to get chart data from `@elastic/charts` * requires `window._echDebugStateFlag` to be true */ - public async getChartDebugData(dataTestSubj?: string): Promise { - const chart = await this.getChart(dataTestSubj); + public async getChartDebugData( + dataTestSubj?: string, + match: number = 0 + ): Promise { + const chart = await this.getChart(dataTestSubj, undefined, match); try { const visContainer = await chart.findByCssSelector('.echChartStatus'); diff --git a/test/tsconfig.json b/test/tsconfig.json index 83c456d4bb8ae1..5a0d2670a843cb 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../src/plugins/bfetch/tsconfig.json" }, { "path": "../src/plugins/embeddable/tsconfig.json" }, { "path": "../src/plugins/expressions/tsconfig.json" }, + { "path": "../src/plugins/home/tsconfig.json" }, { "path": "../src/plugins/inspector/tsconfig.json" }, { "path": "../src/plugins/kibana_react/tsconfig.json" }, { "path": "../src/plugins/kibana_usage_collection/tsconfig.json" }, @@ -18,6 +19,7 @@ { "path": "../src/plugins/navigation/tsconfig.json" }, { "path": "../src/plugins/newsfeed/tsconfig.json" }, { "path": "../src/plugins/saved_objects/tsconfig.json" }, + { "path": "../src/plugins/saved_objects_tagging_oss/tsconfig.json" }, { "path": "../src/plugins/telemetry_collection_manager/tsconfig.json" }, { "path": "../src/plugins/telemetry/tsconfig.json" }, { "path": "../src/plugins/ui_actions/tsconfig.json" }, diff --git a/tsconfig.json b/tsconfig.json index fd76cd26744011..75e1b097c734f4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "src/plugins/dev_tools/**/*", "src/plugins/embeddable/**/*", "src/plugins/expressions/**/*", + "src/plugins/home/**/*", "src/plugins/inspector/**/*", "src/plugins/kibana_legacy/**/*", "src/plugins/kibana_react/**/*", @@ -21,6 +22,7 @@ "src/plugins/newsfeed/**/*", "src/plugins/saved_objects/**/*", "src/plugins/security_oss/**/*", + "src/plugins/saved_objects_tagging_oss/**/*", "src/plugins/share/**/*", "src/plugins/telemetry/**/*", "src/plugins/telemetry_collection_manager/**/*", @@ -40,6 +42,7 @@ { "path": "./src/plugins/dev_tools/tsconfig.json" }, { "path": "./src/plugins/embeddable/tsconfig.json" }, { "path": "./src/plugins/expressions/tsconfig.json" }, + { "path": "./src/plugins/home/tsconfig.json" }, { "path": "./src/plugins/inspector/tsconfig.json" }, { "path": "./src/plugins/kibana_legacy/tsconfig.json" }, { "path": "./src/plugins/kibana_react/tsconfig.json" }, @@ -48,6 +51,7 @@ { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, { "path": "./src/plugins/saved_objects/tsconfig.json" }, + { "path": "./src/plugins/saved_objects_tagging_oss/tsconfig.json" }, { "path": "./src/plugins/security_oss/tsconfig.json" }, { "path": "./src/plugins/share/tsconfig.json" }, { "path": "./src/plugins/telemetry/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 1e14c5618f5430..282bf7fb1f0116 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -7,6 +7,7 @@ { "path": "./src/plugins/dev_tools/tsconfig.json" }, { "path": "./src/plugins/embeddable/tsconfig.json" }, { "path": "./src/plugins/expressions/tsconfig.json" }, + { "path": "./src/plugins/home/tsconfig.json" }, { "path": "./src/plugins/inspector/tsconfig.json" }, { "path": "./src/plugins/kibana_legacy/tsconfig.json" }, { "path": "./src/plugins/kibana_react/tsconfig.json" }, @@ -15,6 +16,7 @@ { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, { "path": "./src/plugins/saved_objects/tsconfig.json" }, + { "path": "./src/plugins/saved_objects_tagging_oss/tsconfig.json" }, { "path": "./src/plugins/security_oss/tsconfig.json" }, { "path": "./src/plugins/share/tsconfig.json" }, { "path": "./src/plugins/telemetry/tsconfig.json" }, diff --git a/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts b/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts index 1c7320d3df6f31..5e26a5776f9fe2 100644 --- a/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts +++ b/x-pack/plugins/alerts/server/invalidate_pending_api_keys/task.ts @@ -12,7 +12,7 @@ import { SavedObjectsClientContract, } from 'kibana/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; -import { InvalidateAPIKeyParams, SecurityPluginStart } from '../../../security/server'; +import { InvalidateAPIKeysParams, SecurityPluginStart } from '../../../security/server'; import { RunContext, TaskManagerSetupContract, @@ -27,8 +27,8 @@ import { InvalidatePendingApiKey } from '../types'; const TASK_TYPE = 'alerts_invalidate_api_keys'; export const TASK_ID = `Alerts-${TASK_TYPE}`; -const invalidateAPIKey = async ( - params: InvalidateAPIKeyParams, +const invalidateAPIKeys = async ( + params: InvalidateAPIKeysParams, securityPluginStart?: SecurityPluginStart ): Promise => { if (!securityPluginStart) { @@ -193,30 +193,35 @@ async function invalidateApiKeys( encryptedSavedObjectsClient: EncryptedSavedObjectsClient, securityPluginStart?: SecurityPluginStart ) { - // TODO: This could probably send a single request to ES now that the invalidate API supports multiple ids in a single request let totalInvalidated = 0; - await Promise.all( + const apiKeyIds = await Promise.all( apiKeysToInvalidate.saved_objects.map(async (apiKeyObj) => { const decryptedApiKey = await encryptedSavedObjectsClient.getDecryptedAsInternalUser( 'api_key_pending_invalidation', apiKeyObj.id ); - const apiKeyId = decryptedApiKey.attributes.apiKeyId; - const response = await invalidateAPIKey({ id: apiKeyId }, securityPluginStart); - if (response.apiKeysEnabled === true && response.result.error_count > 0) { - logger.error(`Failed to invalidate API Key [id="${apiKeyObj.attributes.apiKeyId}"]`); - } else { - try { - await savedObjectsClient.delete('api_key_pending_invalidation', apiKeyObj.id); - totalInvalidated++; - } catch (err) { - logger.error( - `Failed to cleanup api key "${apiKeyObj.attributes.apiKeyId}". Error: ${err.message}` - ); - } - } + return decryptedApiKey.attributes.apiKeyId; }) ); - logger.debug(`Total invalidated api keys "${totalInvalidated}"`); + if (apiKeyIds.length > 0) { + const response = await invalidateAPIKeys({ ids: apiKeyIds }, securityPluginStart); + if (response.apiKeysEnabled === true && response.result.error_count > 0) { + logger.error(`Failed to invalidate API Keys [ids="${apiKeyIds.join(', ')}"]`); + } else { + await Promise.all( + apiKeysToInvalidate.saved_objects.map(async (apiKeyObj) => { + try { + await savedObjectsClient.delete('api_key_pending_invalidation', apiKeyObj.id); + totalInvalidated++; + } catch (err) { + logger.error( + `Failed to delete invalidated API key "${apiKeyObj.attributes.apiKeyId}". Error: ${err.message}` + ); + } + }) + ); + } + } + logger.debug(`Total invalidated API keys "${totalInvalidated}"`); return totalInvalidated; } diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx index b0ef28fbb7b0d2..0568930f8157d3 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx @@ -13,7 +13,7 @@ import { import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../../../shared/Links/apm/TransactionDetailLink'; import { StickyProperties } from '../../../../../shared/StickyProperties'; -import { ServiceOrTransactionsOverviewLink } from '../../../../../shared/Links/apm/service_transactions_overview'; +import { ServiceOrTransactionsOverviewLink } from '../../../../../shared/Links/apm/service_transactions_overview_link'; interface Props { transaction?: Transaction; diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx index ca5b4938ff42ee..6bcb9a764a352e 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx @@ -15,7 +15,7 @@ import { import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n'; import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { StickyProperties } from '../../../../../../shared/StickyProperties'; -import { ServiceOrTransactionsOverviewLink } from '../../../../../../shared/Links/apm/service_transactions_overview'; +import { ServiceOrTransactionsOverviewLink } from '../../../../../../shared/Links/apm/service_transactions_overview_link'; import { TransactionDetailLink } from '../../../../../../shared/Links/apm/TransactionDetailLink'; interface Props { diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx index 961320baa6a4ec..958d25a88434c4 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx +++ b/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx @@ -10,20 +10,20 @@ import React, { ReactNode } from 'react'; import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name'; import { enableServiceOverview } from '../../../../common/ui_settings_keys'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useErrorOverviewHref } from '../../shared/Links/apm/ErrorOverviewLink'; import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink'; import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; -import { useServiceOrTransactionsOverviewHref } from '../../shared/Links/apm/service_transactions_overview'; +import { useTransactionsOverviewHref } from '../../shared/Links/apm/transaction_overview_link'; import { MainTabs } from '../../shared/main_tabs'; import { ErrorGroupOverview } from '../ErrorGroupOverview'; import { ServiceMap } from '../ServiceMap'; -import { ServiceMetrics } from '../service_metrics'; import { ServiceNodeOverview } from '../ServiceNodeOverview'; +import { ServiceMetrics } from '../service_metrics'; import { ServiceOverview } from '../service_overview'; import { TransactionOverview } from '../transaction_overview'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; interface Tab { key: string; @@ -60,7 +60,7 @@ export function ServiceDetailTabs({ serviceName, tab }: Props) { const transactionsTab = { key: 'transactions', - href: useServiceOrTransactionsOverviewHref(serviceName), + href: useTransactionsOverviewHref(serviceName), text: i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { defaultMessage: 'Transactions', }), diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx index 8ae286b2404ae3..1f8ff6fdcaf19d 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/ServiceList/index.tsx @@ -27,7 +27,7 @@ import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { fontSizes, px, truncate, unit } from '../../../../style/variables'; import { ManagedTable, ITableColumn } from '../../../shared/ManagedTable'; import { EnvironmentBadge } from '../../../shared/EnvironmentBadge'; -import { ServiceOrTransactionsOverviewLink } from '../../../shared/Links/apm/service_transactions_overview'; +import { ServiceOrTransactionsOverviewLink } from '../../../shared/Links/apm/service_transactions_overview_link'; import { AgentIcon } from '../../../shared/AgentIcon'; import { HealthBadge } from './HealthBadge'; import { ServiceListMetric } from './ServiceListMetric'; diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx index 4b262f1f51319a..7c90ea68d6f848 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx @@ -32,7 +32,7 @@ import { px, unit } from '../../../../style/variables'; import { SparkPlot } from '../../../shared/charts/spark_plot'; import { ImpactBar } from '../../../shared/ImpactBar'; import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; -import { TransactionOverviewLink } from '../../../shared/Links/apm/transaction_overview_ink'; +import { TransactionOverviewLink } from '../../../shared/Links/apm/transaction_overview_link'; import { TableFetchWrapper } from '../../../shared/table_fetch_wrapper'; import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; import { ServiceOverviewTableContainer } from '../service_overview_table_container'; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx index de7130e8786081..8031b6088d420d 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx @@ -3,11 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; +import { APMLinkExtendProps, useAPMHref } from './APMLink'; const persistedFilters: Array = [ 'host', @@ -25,13 +24,6 @@ interface Props extends APMLinkExtendProps { } export function MetricOverviewLink({ serviceName, ...rest }: Props) { - const { urlParams } = useUrlParams(); - - return ( - - ); + const href = useMetricOverviewHref(serviceName); + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx index ae5dc86608a90e..670b7137219e17 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx @@ -3,15 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; - -function pathFor(serviceName?: string) { - return serviceName ? `/services/${serviceName}/service-map` : '/service-map'; -} +import { APMLinkExtendProps, useAPMHref } from './APMLink'; export function useServiceMapHref(serviceName?: string) { - return useAPMHref(pathFor(serviceName)); + const pathFor = serviceName + ? `/services/${serviceName}/service-map` + : '/service-map'; + return useAPMHref(pathFor); } interface ServiceMapLinkProps extends APMLinkExtendProps { @@ -19,6 +19,6 @@ interface ServiceMapLinkProps extends APMLinkExtendProps { } export function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) { - const path = pathFor(serviceName); - return ; + const href = useServiceMapHref(serviceName); + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx index c107b436717c2d..279c038d95a80c 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx @@ -3,11 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; import { APMQueryParams } from '../url_helpers'; +import { useAPMHref } from './APMLink'; const persistedFilters: Array = [ 'host', @@ -19,19 +16,3 @@ const persistedFilters: Array = [ export function useServiceNodeOverviewHref(serviceName: string) { return useAPMHref(`/services/${serviceName}/nodes`, persistedFilters); } - -interface Props extends APMLinkExtendProps { - serviceName: string; -} - -export function ServiceNodeOverviewLink({ serviceName, ...rest }: Props) { - const { urlParams } = useUrlParams(); - - return ( - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx index caa1498e6df87a..3cb0009a12c945 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx @@ -9,11 +9,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; +import { useAPMHref } from './APMLink'; const persistedFilters: Array = [ 'transactionResult', @@ -25,15 +22,3 @@ const persistedFilters: Array = [ export function useTraceOverviewHref() { return useAPMHref('/traces', persistedFilters); } - -export function TraceOverviewLink(props: APMLinkExtendProps) { - const { urlParams } = useUrlParams(); - - return ( - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx index 318a1590be77c3..c3b80cbeb701b8 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx @@ -9,22 +9,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; +import { useAPMHref } from './APMLink'; const persistedFilters: Array = ['host', 'agentName']; export function useServiceInventoryHref() { return useAPMHref('/services', persistedFilters); } - -export function ServiceInventoryLink(props: APMLinkExtendProps) { - const { urlParams } = useUrlParams(); - - const query = pickKeys(urlParams as APMQueryParams, ...persistedFilters); - - return ; -} diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx index 1f74f1f9890cf6..ba53243a6bc750 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx @@ -8,11 +8,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; +import { APMLinkExtendProps, useAPMHref } from './APMLink'; interface ServiceOverviewLinkProps extends APMLinkExtendProps { serviceName: string; @@ -30,13 +29,6 @@ export function ServiceOverviewLink({ serviceName, ...rest }: ServiceOverviewLinkProps) { - const { urlParams } = useUrlParams(); - - return ( - - ); + const href = useServiceOverviewHref(serviceName); + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx new file mode 100644 index 00000000000000..4c826ecf376827 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.test.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { render } from '@testing-library/react'; +import { renderHook } from '@testing-library/react-hooks'; +import { createMemoryHistory } from 'history'; +import React from 'react'; +import { Router } from 'react-router-dom'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; +import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; +import { + ServiceOrTransactionsOverviewLink, + useServiceOrTransactionsOverviewHref, +} from './service_transactions_overview_link'; + +const history = createMemoryHistory(); + +function wrapper({ queryParams }: { queryParams?: Record }) { + return ({ children }: { children: React.ReactElement }) => ( + + + + {children} + + + + ); +} + +describe('Service or transactions overview link', () => { + describe('useServiceOrTransactionsOverviewHref', () => { + it('returns service link', () => { + const { result } = renderHook( + () => useServiceOrTransactionsOverviewHref('foo'), + { wrapper: wrapper({}) } + ); + expect(result.current).toEqual('/basepath/app/apm/services/foo'); + }); + + it('returns service link with persisted query items', () => { + const { result } = renderHook( + () => useServiceOrTransactionsOverviewHref('foo'), + { wrapper: wrapper({ queryParams: { latencyAggregationType: 'avg' } }) } + ); + expect(result.current).toEqual( + '/basepath/app/apm/services/foo?latencyAggregationType=avg' + ); + }); + }); + describe('ServiceOrTransactionsOverviewLink', () => { + function getHref(container: HTMLElement) { + return ((container as HTMLDivElement).children[0] as HTMLAnchorElement) + .href; + } + it('returns service link', () => { + const Component = wrapper({}); + const { container } = render( + + + Service name + + + ); + expect(getHref(container)).toEqual( + 'http://localhost/basepath/app/apm/services/foo' + ); + }); + + it('returns service link with persisted query items', () => { + const Component = wrapper({ + queryParams: { latencyAggregationType: 'avg' }, + }); + const { container } = render( + + + Service name + + + ); + expect(getHref(container)).toEqual( + 'http://localhost/basepath/app/apm/services/foo?latencyAggregationType=avg' + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx similarity index 59% rename from x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx index 24a78e5d64749a..8b96ba8ab233a2 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_transactions_overview_link.tsx @@ -3,11 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; import { APMQueryParams } from '../url_helpers'; +import { APMLinkExtendProps, useAPMHref } from './APMLink'; const persistedFilters: Array = [ 'transactionResult', @@ -19,7 +18,7 @@ const persistedFilters: Array = [ ]; export function useServiceOrTransactionsOverviewHref(serviceName: string) { - return useAPMHref(`/services/${serviceName}/transactions`, persistedFilters); + return useAPMHref(`/services/${serviceName}`, persistedFilters); } interface Props extends APMLinkExtendProps { @@ -30,13 +29,6 @@ export function ServiceOrTransactionsOverviewLink({ serviceName, ...rest }: Props) { - const { urlParams } = useUrlParams(); - - return ( - - ); + const href = useServiceOrTransactionsOverviewHref(serviceName); + return ; } diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.test.tsx new file mode 100644 index 00000000000000..3ab6feaf5ae12b --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.test.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { render } from '@testing-library/react'; +import { renderHook } from '@testing-library/react-hooks'; +import { createMemoryHistory } from 'history'; +import React from 'react'; +import { Router } from 'react-router-dom'; +import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; +import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; +import { + TransactionOverviewLink, + useTransactionsOverviewHref, +} from './transaction_overview_link'; + +const history = createMemoryHistory(); + +function wrapper({ queryParams }: { queryParams?: Record }) { + return ({ children }: { children: React.ReactElement }) => ( + + + + {children} + + + + ); +} + +describe('Transactions overview link', () => { + describe('useTransactionsOverviewHref', () => { + it('returns transaction link', () => { + const { result } = renderHook(() => useTransactionsOverviewHref('foo'), { + wrapper: wrapper({}), + }); + expect(result.current).toEqual( + '/basepath/app/apm/services/foo/transactions' + ); + }); + + it('returns transaction link with persisted query items', () => { + const { result } = renderHook(() => useTransactionsOverviewHref('foo'), { + wrapper: wrapper({ queryParams: { latencyAggregationType: 'avg' } }), + }); + expect(result.current).toEqual( + '/basepath/app/apm/services/foo/transactions?latencyAggregationType=avg' + ); + }); + }); + describe('TransactionOverviewLink', () => { + function getHref(container: HTMLElement) { + return ((container as HTMLDivElement).children[0] as HTMLAnchorElement) + .href; + } + it('returns transaction link', () => { + const Component = wrapper({}); + const { container } = render( + + + Service name + + + ); + expect(getHref(container)).toEqual( + 'http://localhost/basepath/app/apm/services/foo/transactions' + ); + }); + + it('returns transaction link with persisted query items', () => { + const Component = wrapper({ + queryParams: { latencyAggregationType: 'avg' }, + }); + const { container } = render( + + + Service name + + + ); + expect(getHref(container)).toEqual( + 'http://localhost/basepath/app/apm/services/foo/transactions?latencyAggregationType=avg' + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx similarity index 56% rename from x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx index d2978b3c02d532..cd6d70b2e2e6d4 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_ink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/transaction_overview_link.tsx @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; import React from 'react'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; -import { APMLink, APMLinkExtendProps } from './APMLink'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { APMQueryParams } from '../url_helpers'; +import { APMLinkExtendProps, useAPMHref } from './APMLink'; interface Props extends APMLinkExtendProps { serviceName: string; @@ -18,14 +17,11 @@ const persistedFilters: Array = [ 'latencyAggregationType', ]; -export function TransactionOverviewLink({ serviceName, ...rest }: Props) { - const { urlParams } = useUrlParams(); +export function useTransactionsOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/transactions`, persistedFilters); +} - return ( - - ); +export function TransactionOverviewLink({ serviceName, ...rest }: Props) { + const href = useTransactionsOverviewHref(serviceName); + return ; } diff --git a/x-pack/plugins/fleet/server/services/agents/unenroll.ts b/x-pack/plugins/fleet/server/services/agents/unenroll.ts index 60533e12851410..9c2b2bdfe7f6d1 100644 --- a/x-pack/plugins/fleet/server/services/agents/unenroll.ts +++ b/x-pack/plugins/fleet/server/services/agents/unenroll.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { chunk } from 'lodash'; import { SavedObjectsClientContract } from 'src/core/server'; import { AgentSOAttributes } from '../../types'; import { AGENT_SAVED_OBJECT_TYPE } from '../../constants'; @@ -76,10 +75,10 @@ export async function forceUnenrollAgent(soClient: SavedObjectsClientContract, a await Promise.all([ agent.access_api_key_id - ? APIKeyService.invalidateAPIKey(soClient, agent.access_api_key_id) + ? APIKeyService.invalidateAPIKeys(soClient, [agent.access_api_key_id]) : undefined, agent.default_api_key_id - ? APIKeyService.invalidateAPIKey(soClient, agent.default_api_key_id) + ? APIKeyService.invalidateAPIKeys(soClient, [agent.default_api_key_id]) : undefined, ]); @@ -124,16 +123,8 @@ export async function forceUnenrollAgents( }); // Invalidate all API keys - // ES doesn't provide a bulk invalidate API, so this could take a long time depending on - // number of keys to invalidate. We run these in batches to avoid overloading ES. if (apiKeys.length) { - const BATCH_SIZE = 500; - const batches = chunk(apiKeys, BATCH_SIZE); - for (const apiKeysBatch of batches) { - await Promise.all( - apiKeysBatch.map((apiKey) => APIKeyService.invalidateAPIKey(soClient, apiKey)) - ); - } + APIKeyService.invalidateAPIKeys(soClient, apiKeys); } // Update the necessary agents diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts index b9d0cf883d35c6..8f67753392e65d 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts @@ -9,7 +9,7 @@ import Boom from '@hapi/boom'; import { SavedObjectsClientContract, SavedObject } from 'src/core/server'; import { EnrollmentAPIKey, EnrollmentAPIKeySOAttributes } from '../../types'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; -import { createAPIKey, invalidateAPIKey } from './security'; +import { createAPIKey, invalidateAPIKeys } from './security'; import { agentPolicyService } from '../agent_policy'; import { appContextService } from '../app_context'; import { normalizeKuery } from '../saved_object'; @@ -66,7 +66,7 @@ export async function getEnrollmentAPIKey(soClient: SavedObjectsClientContract, export async function deleteEnrollmentApiKey(soClient: SavedObjectsClientContract, id: string) { const enrollmentApiKey = await getEnrollmentAPIKey(soClient, id); - await invalidateAPIKey(soClient, enrollmentApiKey.api_key_id); + await invalidateAPIKeys(soClient, [enrollmentApiKey.api_key_id]); await soClient.update(ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, id, { active: false, diff --git a/x-pack/plugins/fleet/server/services/api_keys/index.ts b/x-pack/plugins/fleet/server/services/api_keys/index.ts index d1a4a21dec106c..bc756a311dc78c 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/index.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/index.ts @@ -10,7 +10,7 @@ import { EnrollmentAPIKeySOAttributes, EnrollmentAPIKey } from '../../types'; import { createAPIKey } from './security'; import { escapeSearchQueryPhrase } from '../saved_object'; -export { invalidateAPIKey } from './security'; +export { invalidateAPIKeys } from './security'; export * from './enrollment_api_key'; export async function generateOutputApiKey( diff --git a/x-pack/plugins/fleet/server/services/api_keys/security.ts b/x-pack/plugins/fleet/server/services/api_keys/security.ts index 9a32da3cff46f5..a22776435e930e 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/security.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/security.ts @@ -64,7 +64,7 @@ export async function authenticate(callCluster: CallESAsCurrentUser) { } } -export async function invalidateAPIKey(soClient: SavedObjectsClientContract, id: string) { +export async function invalidateAPIKeys(soClient: SavedObjectsClientContract, ids: string[]) { const adminUser = await outputService.getAdminUser(soClient); if (!adminUser) { throw new Error('No admin user configured'); @@ -88,7 +88,7 @@ export async function invalidateAPIKey(soClient: SavedObjectsClientContract, id: try { const res = await security.authc.apiKeys.invalidate(request, { - id, + ids, }); return res; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/loading_error.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/loading_error.tsx index 32bf79b0231379..afff5442c585c2 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/loading_error.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/phases/shared_fields/data_tier_allocation_field/components/loading_error.tsx @@ -21,6 +21,8 @@ export const LoadingError: FunctionComponent = ({ }) => { return ( <> + + = ({ history }) => { 'xpack.indexLifecycleMgmt.editPolicy.validPolicyNameMessage', { defaultMessage: - 'A policy name cannot start with an underscore and cannot contain a question mark or a space.', + 'A policy name cannot start with an underscore and cannot contain a comma or a space.', } ), validations: policyNameValidations, diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index f69ca798c01b0e..82f491c3890297 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -14,10 +14,12 @@ import { ForLastExpression, } from '../../../../../../triggers_actions_ui/public'; import { - PartialAlertParams, Comparator, isRatioAlert, + PartialAlertParams, + PartialCountAlertParams, PartialCriteria as PartialCriteriaType, + PartialRatioAlertParams, ThresholdType, timeUnitRT, } from '../../../../../common/alerting/logs/log_threshold/types'; @@ -47,7 +49,7 @@ interface LogsContextMeta { const DEFAULT_BASE_EXPRESSION = { timeSize: 5, - timeUnit: 'm', + timeUnit: 'm' as const, }; const DEFAULT_FIELD = 'log.level'; @@ -60,7 +62,9 @@ const createDefaultCriterion = ( ? { field: DEFAULT_FIELD, comparator: Comparator.EQ, value } : { field: undefined, comparator: undefined, value: undefined }; -const createDefaultCountAlertParams = (availableFields: LogIndexField[]) => ({ +const createDefaultCountAlertParams = ( + availableFields: LogIndexField[] +): PartialCountAlertParams => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 75, @@ -69,15 +73,17 @@ const createDefaultCountAlertParams = (availableFields: LogIndexField[]) => ({ criteria: [createDefaultCriterion(availableFields, 'error')], }); -const createDefaultRatioAlertParams = (availableFields: LogIndexField[]) => ({ +const createDefaultRatioAlertParams = ( + availableFields: LogIndexField[] +): PartialRatioAlertParams => ({ ...DEFAULT_BASE_EXPRESSION, count: { value: 2, comparator: Comparator.GT, }, criteria: [ - createDefaultCriterion(availableFields, 'error'), - createDefaultCriterion([], 'warning'), + [createDefaultCriterion(availableFields, 'error')], + [createDefaultCriterion(availableFields, 'warning')], ], }); diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts index e04fe338f34360..dea808a29d1cb3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.test.ts @@ -413,31 +413,6 @@ describe('Log threshold executor', () => { describe('Results processors', () => { describe('Can process ungrouped results', () => { - test('It handles the OK state correctly', () => { - const alertInstanceUpdaterMock = jest.fn(); - const alertParams = { - ...baseAlertParams, - criteria: [positiveCriteria[0]], - }; - const results = { - hits: { - total: { - value: 2, - }, - }, - } as UngroupedSearchQueryResponse; - processUngroupedResults( - results, - alertParams, - alertsMock.createAlertInstanceFactory, - alertInstanceUpdaterMock - ); - // First call, second argument - expect(alertInstanceUpdaterMock.mock.calls[0][1]).toBe(AlertStates.OK); - // First call, third argument - expect(alertInstanceUpdaterMock.mock.calls[0][2]).toBe(undefined); - }); - test('It handles the ALERT state correctly', () => { const alertInstanceUpdaterMock = jest.fn(); const alertParams = { @@ -475,68 +450,6 @@ describe('Log threshold executor', () => { }); describe('Can process grouped results', () => { - test('It handles the OK state correctly', () => { - const alertInstanceUpdaterMock = jest.fn(); - const alertParams = { - ...baseAlertParams, - criteria: [positiveCriteria[0]], - groupBy: ['host.name', 'event.dataset'], - }; - const results = [ - { - key: { - 'host.name': 'i-am-a-host-name', - 'event.dataset': 'i-am-a-dataset', - }, - doc_count: 100, - filtered_results: { - doc_count: 1, - }, - }, - { - key: { - 'host.name': 'i-am-a-host-name', - 'event.dataset': 'i-am-a-dataset', - }, - doc_count: 100, - filtered_results: { - doc_count: 2, - }, - }, - { - key: { - 'host.name': 'i-am-a-host-name', - 'event.dataset': 'i-am-a-dataset', - }, - doc_count: 100, - filtered_results: { - doc_count: 3, - }, - }, - ] as GroupedSearchQueryResponse['aggregations']['groups']['buckets']; - processGroupByResults( - results, - alertParams, - alertsMock.createAlertInstanceFactory, - alertInstanceUpdaterMock - ); - expect(alertInstanceUpdaterMock.mock.calls.length).toBe(3); - // First call, second argument - expect(alertInstanceUpdaterMock.mock.calls[0][1]).toBe(AlertStates.OK); - // First call, third argument - expect(alertInstanceUpdaterMock.mock.calls[0][2]).toBe(undefined); - - // Second call, second argument - expect(alertInstanceUpdaterMock.mock.calls[1][1]).toBe(AlertStates.OK); - // Second call, third argument - expect(alertInstanceUpdaterMock.mock.calls[1][2]).toBe(undefined); - - // Third call, second argument - expect(alertInstanceUpdaterMock.mock.calls[2][1]).toBe(AlertStates.OK); - // Third call, third argument - expect(alertInstanceUpdaterMock.mock.calls[2][2]).toBe(undefined); - }); - test('It handles the ALERT state correctly', () => { const alertInstanceUpdaterMock = jest.fn(); const alertParams = { @@ -583,7 +496,7 @@ describe('Log threshold executor', () => { alertsMock.createAlertInstanceFactory, alertInstanceUpdaterMock ); - expect(alertInstanceUpdaterMock.mock.calls.length).toBe(results.length); + expect(alertInstanceUpdaterMock.mock.calls.length).toBe(2); // First call, second argument expect(alertInstanceUpdaterMock.mock.calls[0][1]).toBe(AlertStates.ALERT); // First call, third argument @@ -600,14 +513,9 @@ describe('Log threshold executor', () => { ]); // Second call, second argument - expect(alertInstanceUpdaterMock.mock.calls[1][1]).toBe(AlertStates.OK); + expect(alertInstanceUpdaterMock.mock.calls[1][1]).toBe(AlertStates.ALERT); // Second call, third argument - expect(alertInstanceUpdaterMock.mock.calls[1][2]).toBe(undefined); - - // Third call, second argument - expect(alertInstanceUpdaterMock.mock.calls[2][1]).toBe(AlertStates.ALERT); - // Third call, third argument - expect(alertInstanceUpdaterMock.mock.calls[2][2]).toEqual([ + expect(alertInstanceUpdaterMock.mock.calls[1][2]).toEqual([ { actionGroup: 'logs.threshold.fired', context: { diff --git a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts index f4a9e8fdef3ffd..0044855a73f5cd 100644 --- a/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/log_threshold/log_threshold_executor.ts @@ -72,7 +72,6 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => const sourceConfiguration = await sources.getSourceConfiguration(savedObjectsClient, 'default'); const indexPattern = sourceConfiguration.configuration.logAlias; const timestampField = sourceConfiguration.configuration.fields.timestamp; - const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); try { const validatedParams = decodeOrThrow(alertParamsRT)(params); @@ -95,10 +94,6 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => ); } } catch (e) { - alertInstance.replaceState({ - alertState: AlertStates.ERROR, - }); - throw new Error(e); } }; @@ -198,11 +193,10 @@ export const processUngroupedResults = ( alertInstaceUpdater: AlertInstanceUpdater ) => { const { count, criteria } = params; - - const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); const documentCount = results.hits.total.value; if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) { + const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -214,8 +208,6 @@ export const processUngroupedResults = ( }, }, ]); - } else { - alertInstaceUpdater(alertInstance, AlertStates.OK); } }; @@ -228,12 +220,12 @@ export const processUngroupedRatioResults = ( ) => { const { count, criteria } = params; - const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); const numeratorCount = numeratorResults.hits.total.value; const denominatorCount = denominatorResults.hits.total.value; const ratio = getRatio(numeratorCount, denominatorCount); if (ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value)) { + const alertInstance = alertInstanceFactory(UNGROUPED_FACTORY_KEY); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -246,8 +238,6 @@ export const processUngroupedRatioResults = ( }, }, ]); - } else { - alertInstaceUpdater(alertInstance, AlertStates.OK); } }; @@ -286,10 +276,10 @@ export const processGroupByResults = ( const groupResults = getReducedGroupByResults(results); groupResults.forEach((group) => { - const alertInstance = alertInstanceFactory(group.name); const documentCount = group.documentCount; if (checkValueAgainstComparatorMap[count.comparator](documentCount, count.value)) { + const alertInstance = alertInstanceFactory(group.name); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -301,8 +291,6 @@ export const processGroupByResults = ( }, }, ]); - } else { - alertInstaceUpdater(alertInstance, AlertStates.OK); } }); }; @@ -320,7 +308,6 @@ export const processGroupByRatioResults = ( const denominatorGroupResults = getReducedGroupByResults(denominatorResults); numeratorGroupResults.forEach((numeratorGroup) => { - const alertInstance = alertInstanceFactory(numeratorGroup.name); const numeratorDocumentCount = numeratorGroup.documentCount; const denominatorGroup = denominatorGroupResults.find( (_group) => _group.name === numeratorGroup.name @@ -333,6 +320,7 @@ export const processGroupByRatioResults = ( ratio !== undefined && checkValueAgainstComparatorMap[count.comparator](ratio, count.value) ) { + const alertInstance = alertInstanceFactory(numeratorGroup.name); alertInstaceUpdater(alertInstance, AlertStates.ALERT, [ { actionGroup: FIRED_ACTIONS.id, @@ -345,8 +333,6 @@ export const processGroupByRatioResults = ( }, }, ]); - } else { - alertInstaceUpdater(alertInstance, AlertStates.OK); } }); }; diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts index c43ed9e1248f62..c2506e04e2f25f 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.test.ts @@ -289,7 +289,7 @@ describe('API Keys', () => { it('returns null when security feature is disabled', async () => { mockLicense.isEnabled.mockReturnValue(false); const result = await apiKeys.invalidate(httpServerMock.createKibanaRequest(), { - id: '123', + ids: ['123'], }); expect(result).toBeNull(); expect( @@ -309,7 +309,7 @@ describe('API Keys', () => { }) ); const result = await apiKeys.invalidate(httpServerMock.createKibanaRequest(), { - id: '123', + ids: ['123'], }); expect(result).toEqual({ invalidated_api_keys: ['api-key-id-1'], @@ -323,7 +323,7 @@ describe('API Keys', () => { }); }); - it(`Only passes id as a parameter`, async () => { + it(`Only passes ids as a parameter`, async () => { mockLicense.isEnabled.mockReturnValue(true); mockScopedClusterClient.asCurrentUser.security.invalidateApiKey.mockResolvedValueOnce( securityMock.createApiResponse({ @@ -335,7 +335,7 @@ describe('API Keys', () => { }) ); const result = await apiKeys.invalidate(httpServerMock.createKibanaRequest(), { - id: '123', + ids: ['123'], name: 'abc', } as any); expect(result).toEqual({ @@ -354,7 +354,7 @@ describe('API Keys', () => { describe('invalidateAsInternalUser()', () => { it('returns null when security feature is disabled', async () => { mockLicense.isEnabled.mockReturnValue(false); - const result = await apiKeys.invalidateAsInternalUser({ id: '123' }); + const result = await apiKeys.invalidateAsInternalUser({ ids: ['123'] }); expect(result).toBeNull(); expect(mockClusterClient.asInternalUser.security.invalidateApiKey).not.toHaveBeenCalled(); }); @@ -370,7 +370,7 @@ describe('API Keys', () => { }, }) ); - const result = await apiKeys.invalidateAsInternalUser({ id: '123' }); + const result = await apiKeys.invalidateAsInternalUser({ ids: ['123'] }); expect(result).toEqual({ invalidated_api_keys: ['api-key-id-1'], previously_invalidated_api_keys: [], @@ -383,7 +383,7 @@ describe('API Keys', () => { }); }); - it('Only passes id as a parameter', async () => { + it('Only passes ids as a parameter', async () => { mockLicense.isEnabled.mockReturnValue(true); mockClusterClient.asInternalUser.security.invalidateApiKey.mockResolvedValueOnce( securityMock.createApiResponse({ @@ -395,7 +395,7 @@ describe('API Keys', () => { }) ); const result = await apiKeys.invalidateAsInternalUser({ - id: '123', + ids: ['123'], name: 'abc', } as any); expect(result).toEqual({ diff --git a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts index a42efb678fcea5..7e76634a2cffdc 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/api_keys.ts @@ -39,10 +39,10 @@ interface GrantAPIKeyParams { } /** - * Represents the params for invalidating an API key + * Represents the params for invalidating multiple API keys */ -export interface InvalidateAPIKeyParams { - id: string; +export interface InvalidateAPIKeysParams { + ids: string[]; } /** @@ -222,16 +222,16 @@ export class APIKeys { } /** - * Tries to invalidate an API key. + * Tries to invalidate an API keys. * @param request Request instance. - * @param params The params to invalidate an API key. + * @param params The params to invalidate an API keys. */ - async invalidate(request: KibanaRequest, params: InvalidateAPIKeyParams) { + async invalidate(request: KibanaRequest, params: InvalidateAPIKeysParams) { if (!this.license.isEnabled()) { return null; } - this.logger.debug('Trying to invalidate an API key as current user'); + this.logger.debug(`Trying to invalidate ${params.ids.length} an API key as current user`); let result; try { @@ -240,12 +240,18 @@ export class APIKeys { await this.clusterClient .asScoped(request) .asCurrentUser.security.invalidateApiKey({ - body: { ids: [params.id] }, + body: { ids: params.ids }, }) ).body; - this.logger.debug('API key was invalidated successfully as current user'); + this.logger.debug( + `API keys by ids=[${params.ids.join(', ')}] was invalidated successfully as current user` + ); } catch (e) { - this.logger.error(`Failed to invalidate API key as current user: ${e.message}`); + this.logger.error( + `Failed to invalidate API keys by ids=[${params.ids.join(', ')}] as current user: ${ + e.message + }` + ); throw e; } @@ -253,27 +259,29 @@ export class APIKeys { } /** - * Tries to invalidate an API key by using the internal user. - * @param params The params to invalidate an API key. + * Tries to invalidate the API keys by using the internal user. + * @param params The params to invalidate the API keys. */ - async invalidateAsInternalUser(params: InvalidateAPIKeyParams) { + async invalidateAsInternalUser(params: InvalidateAPIKeysParams) { if (!this.license.isEnabled()) { return null; } - this.logger.debug('Trying to invalidate an API key'); + this.logger.debug(`Trying to invalidate ${params.ids.length} API keys`); let result; try { // Internal user needs `cluster:admin/xpack/security/api_key/invalidate` privilege to use this API result = ( await this.clusterClient.asInternalUser.security.invalidateApiKey({ - body: { ids: [params.id] }, + body: { ids: params.ids }, }) ).body; - this.logger.debug('API key was invalidated successfully'); + this.logger.debug(`API keys by ids=[${params.ids.join(', ')}] was invalidated successfully`); } catch (e) { - this.logger.error(`Failed to invalidate API key: ${e.message}`); + this.logger.error( + `Failed to invalidate API keys by ids=[${params.ids.join(', ')}]: ${e.message}` + ); throw e; } diff --git a/x-pack/plugins/security/server/authentication/api_keys/index.ts b/x-pack/plugins/security/server/authentication/api_keys/index.ts index e0b6d03ea2c425..021eaeb0bd973c 100644 --- a/x-pack/plugins/security/server/authentication/api_keys/index.ts +++ b/x-pack/plugins/security/server/authentication/api_keys/index.ts @@ -9,6 +9,6 @@ export { CreateAPIKeyResult, InvalidateAPIKeyResult, CreateAPIKeyParams, - InvalidateAPIKeyParams, + InvalidateAPIKeysParams, GrantAPIKeyResult, } from './api_keys'; diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index b43ffd86ae5edb..839596eafcc5be 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -28,6 +28,6 @@ export type { CreateAPIKeyResult, InvalidateAPIKeyResult, CreateAPIKeyParams, - InvalidateAPIKeyParams, + InvalidateAPIKeysParams, GrantAPIKeyResult, } from './api_keys'; diff --git a/x-pack/plugins/security/server/index.ts b/x-pack/plugins/security/server/index.ts index 5d51b88d82939b..3233708a5d23b2 100644 --- a/x-pack/plugins/security/server/index.ts +++ b/x-pack/plugins/security/server/index.ts @@ -24,7 +24,7 @@ import { // functions or removal of exports should be considered as a breaking change. export type { CreateAPIKeyResult, - InvalidateAPIKeyParams, + InvalidateAPIKeysParams, InvalidateAPIKeyResult, GrantAPIKeyResult, } from './authentication'; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx index 9046f896259a80..b7516473f1c192 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx @@ -85,6 +85,14 @@ describe('JiraParamsFields renders', () => { }, }, }; + const useGetFieldsByIssueTypeResponseNoPriority = { + ...useGetFieldsByIssueTypeResponse, + fields: { + summary: { allowedValues: [], defaultValue: {} }, + labels: { allowedValues: [], defaultValue: {} }, + description: { allowedValues: [], defaultValue: {} }, + }, + }; beforeEach(() => { jest.clearAllMocks(); @@ -386,5 +394,22 @@ describe('JiraParamsFields renders', () => { expect(comments.simulate('change', emptyComment)); expect(editAction.mock.calls.length).toEqual(1); }); + test('Clears any left behind priority when issueType changes and hasPriority becomes false', () => { + useGetFieldsByIssueTypeMock + .mockReturnValueOnce(useGetFieldsByIssueTypeResponse) + .mockReturnValue(useGetFieldsByIssueTypeResponseNoPriority); + const wrapper = mount(); + wrapper.setProps({ + ...{ + ...defaultProps, + actionParams: { + ...defaultProps.actionParams, + incident: { issueType: '10001' }, + }, + }, + }); + expect(editAction.mock.calls[0][1].incident.priority).toEqual('Medium'); + expect(editAction.mock.calls[1][1].incident.priority).toEqual(null); + }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx index fee8a9115806c0..fa8cddf38f1daa 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.tsx @@ -64,14 +64,27 @@ const JiraParamsFields: React.FunctionComponent { - const newProps = - key !== 'comments' - ? { - incident: { ...incident, [key]: value }, - comments, - } - : { incident, [key]: value }; - editAction('subActionParams', newProps, index); + if (key === 'issueType') { + return editAction( + 'subActionParams', + { + incident: { issueType: value }, + comments, + }, + index + ); + } + if (key === 'comments') { + return editAction('subActionParams', { incident, comments: value }, index); + } + return editAction( + 'subActionParams', + { + incident: { ...incident, [key]: value }, + comments, + }, + index + ); }, [comments, editAction, incident, index] ); @@ -114,6 +127,7 @@ const JiraParamsFields: React.FunctionComponent p.name === incident.priority); + if ((!incident.priority || !doesPriorityExist) && priorities.length > 0) { editSubActionProperty('priority', priorities[0].name ?? ''); } @@ -126,6 +140,12 @@ const JiraParamsFields: React.FunctionComponent { + if (!hasPriority && incident.priority != null) { + editSubActionProperty('priority', null); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [hasPriority]); const labelOptions = useMemo( () => (incident.labels ? incident.labels.map((label: string) => ({ label })) : []), diff --git a/x-pack/test/functional/apps/dashboard/index.ts b/x-pack/test/functional/apps/dashboard/index.ts index 4a893d3f62d934..1ba87f89762a1b 100644 --- a/x-pack/test/functional/apps/dashboard/index.ts +++ b/x-pack/test/functional/apps/dashboard/index.ts @@ -13,6 +13,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./preserve_url')); loadTestFile(require.resolve('./reporting')); loadTestFile(require.resolve('./drilldowns')); + loadTestFile(require.resolve('./sync_colors')); loadTestFile(require.resolve('./_async_dashboard')); }); } diff --git a/x-pack/test/functional/apps/dashboard/sync_colors.ts b/x-pack/test/functional/apps/dashboard/sync_colors.ts new file mode 100644 index 00000000000000..99fe953a84cd4f --- /dev/null +++ b/x-pack/test/functional/apps/dashboard/sync_colors.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DebugState } from '@elastic/charts'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects([ + 'common', + 'dashboard', + 'spaceSelector', + 'header', + 'lens', + 'timePicker', + ]); + const dashboardAddPanel = getService('dashboardAddPanel'); + const filterBar = getService('filterBar'); + const elasticChart = getService('elasticChart'); + + function getColorMapping(debugState: DebugState | null) { + if (!debugState) return {}; + const colorMapping: Record = {}; + debugState.bars?.forEach(({ name, color }) => { + colorMapping[name] = color; + }); + + return colorMapping; + } + + describe('sync colors', function () { + before(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('lens/basic'); + }); + + after(async function () { + await esArchiver.unload('logstash_functional'); + await esArchiver.unload('lens/basic'); + }); + + it('should sync colors on dashboard by default', async function () { + await PageObjects.common.navigateToApp('dashboard'); + await elasticChart.setNewChartUiDebugFlag(true); + await PageObjects.dashboard.clickCreateDashboardPrompt(); + await dashboardAddPanel.clickCreateNewLink(); + await dashboardAddPanel.clickVisType('lens'); + await PageObjects.header.waitUntilLoadingHasFinished(); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'count', + field: 'Records', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'geo.src', + }); + + await PageObjects.lens.save('vis1', true, true); + await PageObjects.header.waitUntilLoadingHasFinished(); + await dashboardAddPanel.clickCreateNewLink(); + await dashboardAddPanel.clickVisType('lens'); + await PageObjects.header.waitUntilLoadingHasFinished(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'count', + field: 'Records', + }); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_splitDimensionPanel > lns-empty-dimension', + operation: 'terms', + field: 'geo.src', + }); + + await filterBar.addFilter('geo.src', 'is not', 'CN'); + + await PageObjects.lens.save('vis2', true, true); + await PageObjects.header.waitUntilLoadingHasFinished(); + const colorMapping1 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(0)); + const colorMapping2 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(1)); + expect(Object.keys(colorMapping1)).to.have.length(6); + expect(Object.keys(colorMapping1)).to.have.length(6); + const panel1Keys = ['CN']; + const panel2Keys = ['PK']; + const sharedKeys = ['IN', 'US', 'ID', 'BR', 'Other']; + // colors for keys exclusive to panel 1 should not occur in panel 2 + panel1Keys.forEach((panel1Key) => { + const assignedColor = colorMapping1[panel1Key]; + expect(Object.values(colorMapping2)).not.to.contain(assignedColor); + }); + // colors for keys exclusive to panel 2 should not occur in panel 1 + panel2Keys.forEach((panel2Key) => { + const assignedColor = colorMapping2[panel2Key]; + expect(Object.values(colorMapping1)).not.to.contain(assignedColor); + }); + // colors for keys used in both panels should be synced + sharedKeys.forEach((sharedKey) => { + expect(colorMapping1[sharedKey]).to.eql(colorMapping2[sharedKey]); + }); + }); + + it('should be possible to disable color sync', async () => { + await PageObjects.dashboard.useColorSync(false); + await PageObjects.header.waitUntilLoadingHasFinished(); + const colorMapping1 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(0)); + const colorMapping2 = getColorMapping(await PageObjects.dashboard.getPanelChartDebugState(1)); + const colorsByOrder1 = Object.values(colorMapping1); + const colorsByOrder2 = Object.values(colorMapping2); + // colors by order of occurence have to be the same + expect(colorsByOrder1).to.eql(colorsByOrder2); + }); + }); +} diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index ccf681f128b10b..5b05628d618a7d 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -13,12 +13,14 @@ { "path": "../../src/plugins/data/tsconfig.json" }, { "path": "../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../src/plugins/expressions/tsconfig.json" }, + { "path": "../../src/plugins/home/tsconfig.json" }, { "path": "../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../src/plugins/kibana_usage_collection/tsconfig.json" }, { "path": "../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../src/plugins/navigation/tsconfig.json" }, { "path": "../../src/plugins/newsfeed/tsconfig.json" }, { "path": "../../src/plugins/saved_objects/tsconfig.json" }, + { "path": "../../src/plugins/saved_objects_tagging_oss/tsconfig.json" }, { "path": "../../src/plugins/share/tsconfig.json" }, { "path": "../../src/plugins/telemetry_collection_manager/tsconfig.json" }, { "path": "../../src/plugins/telemetry/tsconfig.json" }, diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index 613dc9f5b361fd..f6911d12031047 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -25,6 +25,7 @@ { "path": "../src/plugins/dev_tools/tsconfig.json" }, { "path": "../src/plugins/embeddable/tsconfig.json" }, { "path": "../src/plugins/expressions/tsconfig.json" }, + { "path": "../src/plugins/home/tsconfig.json" }, { "path": "../src/plugins/inspector/tsconfig.json" }, { "path": "../src/plugins/kibana_legacy/tsconfig.json" }, { "path": "../src/plugins/kibana_react/tsconfig.json" }, @@ -33,6 +34,7 @@ { "path": "../src/plugins/navigation/tsconfig.json" }, { "path": "../src/plugins/newsfeed/tsconfig.json" }, { "path": "../src/plugins/saved_objects/tsconfig.json" }, + { "path": "../src/plugins/saved_objects_tagging_oss/tsconfig.json" }, { "path": "../src/plugins/security_oss/tsconfig.json" }, { "path": "../src/plugins/share/tsconfig.json" }, { "path": "../src/plugins/telemetry/tsconfig.json" },