diff --git a/.ci/Jenkinsfile_baseline_capture b/.ci/Jenkinsfile_baseline_capture index 33ecfcd84fd3e6..791cacf7abb4c6 100644 --- a/.ci/Jenkinsfile_baseline_capture +++ b/.ci/Jenkinsfile_baseline_capture @@ -12,12 +12,12 @@ kibanaPipeline(timeoutMinutes: 120) { ]) { parallel([ 'oss-baseline': { - workers.ci(name: 'oss-baseline', size: 's-highmem', ramDisk: true, runErrorReporter: false) { + workers.ci(name: 'oss-baseline', size: 'l', ramDisk: true, runErrorReporter: false) { kibanaPipeline.functionalTestProcess('oss-baseline', './test/scripts/jenkins_baseline.sh')() } }, 'xpack-baseline': { - workers.ci(name: 'xpack-baseline', size: 's-highmem', ramDisk: true, runErrorReporter: false) { + workers.ci(name: 'xpack-baseline', size: 'l', ramDisk: true, runErrorReporter: false) { kibanaPipeline.functionalTestProcess('xpack-baseline', './test/scripts/jenkins_xpack_baseline.sh')() } }, diff --git a/src/plugins/discover/public/application/angular/get_painless_error.ts b/src/plugins/discover/public/application/angular/get_painless_error.ts index e1e98d9df27b19..162dacd3ac3b7c 100644 --- a/src/plugins/discover/public/application/angular/get_painless_error.ts +++ b/src/plugins/discover/public/application/angular/get_painless_error.ts @@ -18,20 +18,77 @@ */ import { i18n } from '@kbn/i18n'; -import { get } from 'lodash'; -export function getPainlessError(error: Error) { - const rootCause: Array<{ lang: string; script: string }> | undefined = get( - error, - 'body.attributes.error.root_cause' - ); - const message: string = get(error, 'body.message'); +interface FailedShards { + shard: number; + index: string; + node: string; + reason: { + type: string; + reason: string; + script_stack: string[]; + script: string; + lang: string; + position: { + offset: number; + start: number; + end: number; + }; + caused_by: { + type: string; + reason: string; + }; + }; +} + +interface EsError { + body: { + statusCode: number; + error: string; + message: string; + attributes?: { + error?: { + root_cause?: [ + { + lang: string; + script: string; + } + ]; + type: string; + reason: string; + caused_by: { + type: string; + reason: string; + phase: string; + grouped: boolean; + failed_shards: FailedShards[]; + }; + }; + }; + }; +} + +export function getCause(error: EsError) { + const cause = error.body?.attributes?.error?.root_cause; + if (cause) { + return cause[0]; + } + + const failedShards = error.body?.attributes?.error?.caused_by?.failed_shards; + + if (failedShards && failedShards[0] && failedShards[0].reason) { + return error.body?.attributes?.error?.caused_by?.failed_shards[0].reason; + } +} + +export function getPainlessError(error: EsError) { + const cause = getCause(error); - if (!rootCause) { + if (!cause) { return; } - const [{ lang, script }] = rootCause; + const { lang, script } = cause; if (lang !== 'painless') { return; @@ -44,6 +101,6 @@ export function getPainlessError(error: Error) { defaultMessage: "Error with Painless scripted field '{script}'.", values: { script }, }), - error: message, + error: error.body?.message, }; } diff --git a/test/functional/apps/discover/_errors.js b/test/functional/apps/discover/_errors.ts similarity index 92% rename from test/functional/apps/discover/_errors.js rename to test/functional/apps/discover/_errors.ts index 614059dc8ac942..9520d652a65d56 100644 --- a/test/functional/apps/discover/_errors.js +++ b/test/functional/apps/discover/_errors.ts @@ -18,8 +18,9 @@ */ import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function ({ getService, getPageObjects }) { +export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 2044053e049f16..2962a5fd2df3b1 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -172,7 +172,7 @@ Array [ }, Object { "key": "transaction_max_spans", - "max": 32000, + "max": undefined, "min": 0, "type": "integer", "validationName": "integerRt", diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index 95892f435e8f9c..e777e1fd09d0bc 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -177,7 +177,6 @@ export const generalSettings: RawSettingDefinition[] = [ key: 'transaction_max_spans', type: 'integer', min: 0, - max: 32000, defaultValue: '500', label: i18n.translate('xpack.apm.agentConfig.transactionMaxSpans.label', { defaultMessage: 'Transaction max spans', diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/ClientSideMonitoringCallout.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/ClientSideMonitoringCallout.tsx new file mode 100644 index 00000000000000..b6938b211994d6 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/ClientSideMonitoringCallout.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiButton, EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; + +export function ClientSideMonitoringCallout() { + const { core } = useApmPluginContext(); + const clientSideMonitoringHref = core.http.basePath.prepend(`/app/csm`); + + return ( + + + {i18n.translate( + 'xpack.apm.transactionOverview.clientSideMonitoring.calloutText', + { + defaultMessage: + 'We are beyond excited to introduce a new experience for analyzing the user experience metrics specifically for your RUM services. It provides insights into the core vitals and visitor breakdown by browser and location. The app is always available in the left sidebar among the other Observability views.', + } + )} + + + + {i18n.translate( + 'xpack.apm.transactionOverview.clientSideMonitoring.linkLabel', + { defaultMessage: 'Take me there' } + )} + + + ); +} diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx index 3e32b0ec23b13d..7c887da6dc5e6a 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx @@ -35,6 +35,8 @@ import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter'; import { TransactionList } from './TransactionList'; import { useRedirect } from './useRedirect'; +import { TRANSACTION_PAGE_LOAD } from '../../../../common/transaction_types'; +import { ClientSideMonitoringCallout } from './ClientSideMonitoringCallout'; function getRedirectLocation({ urlParams, @@ -125,6 +127,12 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) { + {transactionType === TRANSACTION_PAGE_LOAD && ( + <> + + + + )} { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request),zoom:(from:now/w,to:now-4h)))"` + `"/basepath/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(myservicename-mytransactiontype-high_mean_response_time)),refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now-4h))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-test,transaction.type:request)))"` ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts index 66f3903ba849b0..d84f55af993aa2 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.test.ts @@ -28,7 +28,7 @@ describe('useTimeSeriesExplorerHref', () => { }); expect(href).toMatchInlineSnapshot( - `"/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request),zoom:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z')))"` + `"/app/ml#/timeseriesexplorer?_g=(ml:(jobIds:!(apm-production-485b-high_mean_transaction_duration)),refreshInterval:(pause:!t,value:10000),time:(from:'2020-07-29T17:27:29.000Z',to:'2020-07-29T18:45:00.000Z'))&_a=(mlTimeSeriesExplorer:(entities:(service.name:opbeans-java,transaction.type:request)))"` ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts index 3b60962d797edc..0cb87a4f515b6e 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/useTimeSeriesExplorerHref.ts @@ -39,7 +39,6 @@ export function useTimeSeriesExplorerHref({ 'service.name': serviceName, 'transaction.type': transactionType, }, - zoom: time, }, }), } diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx deleted file mode 100644 index 9f112475a4a78f..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; -import { BrowserLineChart } from './BrowserLineChart'; -import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; - -describe('BrowserLineChart', () => { - describe('render', () => { - it('renders', () => { - expect(() => - shallow( - - - - ) - ).not.toThrowError(); - }); - }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx deleted file mode 100644 index 40caf351559183..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useAvgDurationByBrowser } from '../../../../hooks/useAvgDurationByBrowser'; -import { getDurationFormatter } from '../../../../utils/formatters'; -import { - getResponseTimeTickFormatter, - getResponseTimeTooltipFormatter, - getMaxY, -} from './helper'; -import { TransactionLineChart } from './TransactionLineChart'; - -export function BrowserLineChart() { - const { data } = useAvgDurationByBrowser(); - const maxY = getMaxY(data); - const formatter = getDurationFormatter(maxY); - const formatTooltipValue = getResponseTimeTooltipFormatter(formatter); - const tickFormatY = getResponseTimeTickFormatter(formatter); - - return ( - <> - - - {i18n.translate( - 'xpack.apm.metrics.pageLoadCharts.avgPageLoadByBrowser', - { - defaultMessage: 'Avg. page load duration distribution by browser', - } - )} - - - - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx deleted file mode 100644 index 69d4e8109dfbff..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { asDuration, asInteger } from '../../../../../utils/formatters'; -import { fontSizes } from '../../../../../style/variables'; - -export function ChoroplethToolTip({ - name, - value, - docCount, -}: { - name: string; - value: number; - docCount: number; -}) { - return ( -
-
{name}
-
- {i18n.translate( - 'xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.avgPageLoadDuration', - { - defaultMessage: 'Avg. page load duration:', - } - )} -
-
- {asDuration(value)} -
-
- ( - {i18n.translate( - 'xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.countPageLoads', - { - values: { docCount: asInteger(docCount) }, - defaultMessage: '{docCount} page loads', - } - )} - ) -
-
- ); -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx deleted file mode 100644 index 965cb2ae4f50ad..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { - useState, - useEffect, - useRef, - useCallback, - useMemo, -} from 'react'; -import { Map, NavigationControl, Popup } from 'mapbox-gl'; -import 'mapbox-gl/dist/mapbox-gl.css'; -import { shade, tint } from 'polished'; -import { EuiTheme } from '../../../../../../../observability/public'; -import { useTheme } from '../../../../../hooks/useTheme'; -import { ChoroplethToolTip } from './ChoroplethToolTip'; - -interface ChoroplethItem { - key: string; - value: number; - docCount: number; -} - -interface Tooltip { - name: string; - value: number; - docCount: number; -} - -interface WorldCountryFeatureProperties { - name: string; - iso2: string; - iso3: string; -} - -interface Props { - items: ChoroplethItem[]; -} - -const CHOROPLETH_LAYER_ID = 'choropleth_layer'; -const CHOROPLETH_POLYGONS_SOURCE_ID = 'choropleth_polygons'; -const GEOJSON_KEY_PROPERTY = 'iso2'; -const MAPBOX_STYLE = - 'https://tiles.maps.elastic.co/styles/osm-bright-desaturated/style.json'; -const GEOJSON_SOURCE = - 'https://vector.maps.elastic.co/files/world_countries_v1.geo.json?elastic_tile_service_tos=agree&my_app_name=ems-landing&my_app_version=7.2.0'; - -export function getProgressionColor(scale: number, theme: EuiTheme) { - const baseColor = theme.eui.euiColorPrimary; - const adjustedScale = 0.75 * scale + 0.05; // prevents pure black & white as min/max colors. - if (adjustedScale < 0.5) { - return tint(adjustedScale * 2, baseColor); - } - if (adjustedScale > 0.5) { - return shade(1 - (adjustedScale - 0.5) * 2, baseColor); - } - return baseColor; -} - -const getMin = (items: ChoroplethItem[]) => - Math.min(...items.map((item) => item.value)); - -const getMax = (items: ChoroplethItem[]) => - Math.max(...items.map((item) => item.value)); - -export function ChoroplethMap(props: Props) { - const theme = useTheme(); - const { items } = props; - const containerRef = useRef(null); - const [map, setMap] = useState(null); - const popupRef = useRef(null); - const popupContainerRef = useRef(null); - const [tooltipState, setTooltipState] = useState(null); - const [min, max] = useMemo(() => [getMin(items), getMax(items)], [items]); - - // converts an item value to a scaled value between 0 and 1 - const getValueScale = useCallback( - (value: number) => (value - min) / (max - min), - [max, min] - ); - - const controlScrollZoomOnWheel = useCallback((event: WheelEvent) => { - if (event.ctrlKey || event.metaKey) { - event.preventDefault(); - } else { - event.stopPropagation(); - } - }, []); - - // side effect creates a new mouseover handler referencing new component state - // and replaces the old one stored in `updateTooltipStateOnMousemoveRef` - useEffect(() => { - const updateTooltipStateOnMousemove = (event: mapboxgl.MapMouseEvent) => { - const isMapQueryable = - map && - popupRef.current && - items.length && - map.getLayer(CHOROPLETH_LAYER_ID); - - if (!isMapQueryable) { - return; - } - (popupRef.current as Popup).setLngLat(event.lngLat); - const hoverFeatures = (map as Map).queryRenderedFeatures(event.point, { - layers: [CHOROPLETH_LAYER_ID], - }); - - if (tooltipState && hoverFeatures.length === 0) { - return setTooltipState(null); - } - - const featureProperties = hoverFeatures[0] - .properties as WorldCountryFeatureProperties; - - if (tooltipState && tooltipState.name === featureProperties.name) { - return; - } - - const item = items.find( - ({ key }) => - featureProperties && key === featureProperties[GEOJSON_KEY_PROPERTY] - ); - - if (item) { - return setTooltipState({ - name: featureProperties.name, - value: item.value, - docCount: item.docCount, - }); - } - - setTooltipState(null); - }; - updateTooltipStateOnMousemoveRef.current = updateTooltipStateOnMousemove; - }, [map, items, tooltipState]); - - const updateTooltipStateOnMousemoveRef = useRef( - (_event: mapboxgl.MapMouseEvent & mapboxgl.EventData) => {} - ); - - // initialization side effect, only runs once - useEffect(() => { - if (containerRef.current === null) { - return; - } - - // set up Map object - const mapboxMap = new Map({ - attributionControl: false, - container: containerRef.current, - dragRotate: false, - touchZoomRotate: false, - zoom: 0.85, - center: { lng: 0, lat: 30 }, - style: MAPBOX_STYLE, - }); - - mapboxMap.addControl( - new NavigationControl({ showCompass: false }), - 'top-left' - ); - - // set up Popup object - popupRef.current = new Popup({ - closeButton: false, - closeOnClick: false, - }); - - // always use the current handler which changes with component state - mapboxMap.on('mousemove', (...args) => - updateTooltipStateOnMousemoveRef.current(...args) - ); - mapboxMap.on('mouseout', () => { - setTooltipState(null); - }); - - // only scroll zoom when key is pressed - const canvasElement = mapboxMap.getCanvas(); - canvasElement.addEventListener('wheel', controlScrollZoomOnWheel); - - mapboxMap.on('load', () => { - mapboxMap.addSource(CHOROPLETH_POLYGONS_SOURCE_ID, { - type: 'geojson', - data: GEOJSON_SOURCE, - }); - setMap(mapboxMap); - }); - - // cleanup function called when component unmounts - return () => { - canvasElement.removeEventListener('wheel', controlScrollZoomOnWheel); - }; - }, [controlScrollZoomOnWheel]); - - // side effect replaces choropleth layer with new one on items changes - useEffect(() => { - if (!map) { - return; - } - - // find first symbol layer to place new layer in correct order - const symbolLayer = (map.getStyle().layers || []).find( - ({ type }) => type === 'symbol' - ); - - if (map.getLayer(CHOROPLETH_LAYER_ID)) { - map.removeLayer(CHOROPLETH_LAYER_ID); - } - - if (items.length === 0) { - return; - } - - const stops = items.map(({ key, value }) => [ - key, - getProgressionColor(getValueScale(value), theme), - ]); - - const fillColor: mapboxgl.FillPaint['fill-color'] = { - property: GEOJSON_KEY_PROPERTY, - stops, - type: 'categorical', - default: 'transparent', - }; - - map.addLayer( - { - id: CHOROPLETH_LAYER_ID, - type: 'fill', - source: CHOROPLETH_POLYGONS_SOURCE_ID, - layout: {}, - paint: { - 'fill-opacity': 0.75, - 'fill-color': fillColor, - }, - }, - symbolLayer ? symbolLayer.id : undefined - ); - }, [map, items, theme, getValueScale]); - - // side effect to only render the Popup when hovering a region with a matching item - useEffect(() => { - if (!(popupContainerRef.current && map && popupRef.current)) { - return; - } - if (tooltipState) { - popupRef.current.setDOMContent(popupContainerRef.current).addTo(map); - if (popupContainerRef.current.parentElement) { - popupContainerRef.current.parentElement.style.pointerEvents = 'none'; - } - } else { - popupRef.current.remove(); - } - }, [map, tooltipState]); - - // render map container and tooltip in a hidden container - return ( -
-
-
-
- {tooltipState ? : null} -
-
-
- ); -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx deleted file mode 100644 index 2dd3d058e98b8a..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiTitle } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import { useAvgDurationByCountry } from '../../../../../hooks/useAvgDurationByCountry'; - -import { ChoroplethMap } from '../ChoroplethMap'; - -export function DurationByCountryMap() { - const { data } = useAvgDurationByCountry(); - - return ( - <> - {' '} - - - {i18n.translate( - 'xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel', - { - defaultMessage: 'Avg. page load duration distribution by country', - } - )} - - - - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index 6ba080a07b9d38..30ee0ba3eaa1f0 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -26,8 +26,6 @@ import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { ITransactionChartData } from '../../../../selectors/chartSelectors'; import { asDecimal, tpmUnit } from '../../../../utils/formatters'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; -import { BrowserLineChart } from './BrowserLineChart'; -import { DurationByCountryMap } from './DurationByCountryMap'; import { ErroneousTransactionsRateChart } from '../ErroneousTransactionsRateChart'; import { TransactionBreakdown } from '../../TransactionBreakdown'; import { @@ -120,24 +118,6 @@ export function TransactionCharts({ - - {transactionType === TRANSACTION_PAGE_LOAD && ( - <> - - - - - - - - - - - - - - - )} ); } diff --git a/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.test.tsx b/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.test.tsx deleted file mode 100644 index bb947e307437eb..00000000000000 --- a/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.test.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import theme from '@elastic/eui/dist/eui_theme_light.json'; -import * as useFetcherModule from './useFetcher'; -import { useAvgDurationByBrowser } from './useAvgDurationByBrowser'; -import React, { ReactNode } from 'react'; -import { MemoryRouter } from 'react-router-dom'; - -function Wrapper({ children }: { children?: ReactNode }) { - return {children}; -} - -describe('useAvgDurationByBrowser', () => { - it('returns data', () => { - const data = [ - { title: 'Other', data: [{ x: 1572530100000, y: 130010.8947368421 }] }, - ]; - jest.spyOn(useFetcherModule, 'useFetcher').mockReturnValueOnce({ - data, - refetch: () => {}, - status: 'success' as useFetcherModule.FETCH_STATUS, - }); - const { result } = renderHook(() => useAvgDurationByBrowser(), { - wrapper: Wrapper, - }); - - expect(result.current.data).toEqual([ - { - color: theme.euiColorVis0, - data: [{ x: 1572530100000, y: 130010.8947368421 }], - title: 'Other', - type: 'linemark', - }, - ]); - }); -}); diff --git a/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts b/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts deleted file mode 100644 index 78dc4210711ef6..00000000000000 --- a/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { useParams } from 'react-router-dom'; -import { getVizColorForIndex } from '../../common/viz_colors'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AvgDurationByBrowserAPIResponse } from '../../server/lib/transactions/avg_duration_by_browser'; -import { TimeSeries } from '../../typings/timeseries'; -import { useFetcher } from './useFetcher'; -import { useUrlParams } from './useUrlParams'; - -function toTimeSeries(data?: AvgDurationByBrowserAPIResponse): TimeSeries[] { - if (!data) { - return []; - } - - return data.map((item, index) => { - return { - ...item, - color: getVizColorForIndex(index, theme), - type: 'linemark', - }; - }); -} - -export function useAvgDurationByBrowser() { - const { serviceName } = useParams<{ serviceName?: string }>(); - const { - urlParams: { start, end, transactionName }, - uiFilters, - } = useUrlParams(); - - const { data, error, status } = useFetcher( - (callApmApi) => { - if (serviceName && start && end) { - return callApmApi({ - pathname: - '/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_browser', - params: { - path: { serviceName }, - query: { - start, - end, - transactionName, - uiFilters: JSON.stringify(uiFilters), - }, - }, - }); - } - }, - [serviceName, start, end, transactionName, uiFilters] - ); - - return { - data: toTimeSeries(data), - status, - error, - }; -} diff --git a/x-pack/plugins/apm/public/hooks/useAvgDurationByCountry.ts b/x-pack/plugins/apm/public/hooks/useAvgDurationByCountry.ts deleted file mode 100644 index 983f949b72961b..00000000000000 --- a/x-pack/plugins/apm/public/hooks/useAvgDurationByCountry.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { useParams } from 'react-router-dom'; -import { useFetcher } from './useFetcher'; -import { useUrlParams } from './useUrlParams'; - -export function useAvgDurationByCountry() { - const { serviceName } = useParams<{ serviceName?: string }>(); - const { - urlParams: { start, end, transactionName }, - uiFilters, - } = useUrlParams(); - - const { data = [], error, status } = useFetcher( - (callApmApi) => { - if (serviceName && start && end) { - return callApmApi({ - pathname: - '/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_country', - params: { - path: { serviceName }, - query: { - start, - end, - uiFilters: JSON.stringify(uiFilters), - transactionName, - }, - }, - }); - } - }, - [serviceName, start, end, uiFilters, transactionName] - ); - - return { - data, - status, - error, - }; -} diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts deleted file mode 100644 index 44878aa6c1f2e6..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/__fixtures__/responses.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; -import { APMBaseDoc } from '../../../../../typings/es_schemas/raw/apm_base_doc'; -import { - ESSearchResponse, - ESSearchRequest, -} from '../../../../../typings/elasticsearch'; - -export const response = ({ - hits: { - total: 599, - max_score: 0, - hits: [], - }, - took: 4, - timed_out: false, - _shards: { - total: 1, - successful: 1, - skipped: 0, - failed: 0, - }, - aggregations: { - user_agent_keys: { - buckets: [{ key: 'Firefox' }, { key: 'Other' }], - }, - browsers: { - buckets: [ - { - key_as_string: '2019-10-21T04:38:20.000-05:00', - key: 1571650700000, - doc_count: 0, - user_agent: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [], - }, - }, - { - key_as_string: '2019-10-21T04:40:00.000-05:00', - key: 1571650800000, - doc_count: 1, - user_agent: { - doc_count_error_upper_bound: 0, - sum_other_doc_count: 0, - buckets: [ - { - key: 'Other', - doc_count: 1, - avg_duration: { - value: 860425.0, - }, - }, - { - key: 'Firefox', - doc_count: 10, - avg_duration: { - value: 86425.1, - }, - }, - ], - }, - }, - ], - }, - }, -} as unknown) as ESSearchResponse< - APMBaseDoc | Transaction, - ESSearchRequest, - { restTotalHitsAsInt: false } ->; diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts deleted file mode 100644 index aec124e4f46232..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; -import { fetcher } from './fetcher'; - -describe('fetcher', () => { - it('performs a search', async () => { - const search = jest.fn(); - const setup = ({ - apmEventClient: { search }, - indices: {}, - uiFiltersES: [], - } as unknown) as Setup & SetupTimeRange & SetupUIFilters; - - await fetcher({ - serviceName: 'testServiceName', - setup, - searchAggregatedTransactions: false, - }); - - expect(search).toHaveBeenCalled(); - }); -}); diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts deleted file mode 100644 index d40fcaaa02f608..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/fetcher.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ESFilter } from '../../../../typings/elasticsearch'; -import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { - SERVICE_NAME, - TRANSACTION_TYPE, - USER_AGENT_NAME, - TRANSACTION_NAME, -} from '../../../../common/elasticsearch_fieldnames'; -import { rangeFilter } from '../../../../common/utils/range_filter'; -import { getBucketSize } from '../../helpers/get_bucket_size'; -import { Options } from '.'; -import { TRANSACTION_PAGE_LOAD } from '../../../../common/transaction_types'; -import { - getDocumentTypeFilterForAggregatedTransactions, - getTransactionDurationFieldForAggregatedTransactions, - getProcessorEventForAggregatedTransactions, -} from '../../helpers/aggregated_transactions'; - -export type ESResponse = PromiseReturnType; - -export function fetcher(options: Options) { - const { end, apmEventClient, start, uiFiltersES } = options.setup; - const { - serviceName, - searchAggregatedTransactions, - transactionName, - } = options; - const { intervalString } = getBucketSize(start, end); - - const transactionNameFilter = transactionName - ? [{ term: { [TRANSACTION_NAME]: transactionName } }] - : []; - - const filter: ESFilter[] = [ - { term: { [SERVICE_NAME]: serviceName } }, - { term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } }, - { range: rangeFilter(start, end) }, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ...uiFiltersES, - ...transactionNameFilter, - ]; - - const params = { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { bool: { filter } }, - aggs: { - user_agent_keys: { - terms: { - field: USER_AGENT_NAME, - }, - }, - browsers: { - date_histogram: { - extended_bounds: { - max: end, - min: start, - }, - field: '@timestamp', - fixed_interval: intervalString, - min_doc_count: 0, - }, - aggs: { - user_agent: { - terms: { - field: USER_AGENT_NAME, - }, - aggs: { - avg_duration: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }, - }, - }; - - return apmEventClient.search(params); -} diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts deleted file mode 100644 index b8cea3a0322689..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - getTransactionAvgDurationByBrowser, - Options, - AvgDurationByBrowserAPIResponse, -} from '.'; -import * as transformerModule from './transformer'; -import * as fetcherModule from './fetcher'; -import { response } from './__fixtures__/responses'; - -describe('getAvgDurationByBrowser', () => { - it('returns a transformed response', async () => { - const transformer = jest - .spyOn(transformerModule, 'transformer') - .mockReturnValueOnce(({} as unknown) as AvgDurationByBrowserAPIResponse); - const search = () => {}; - const options = ({ - setup: { client: { search }, indices: {}, uiFiltersES: [] }, - } as unknown) as Options; - jest - .spyOn<{ fetcher: any }, 'fetcher'>(fetcherModule, 'fetcher') - .mockResolvedValueOnce(response); - - await getTransactionAvgDurationByBrowser(options); - - expect(transformer).toHaveBeenCalledWith({ response }); - }); -}); diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts deleted file mode 100644 index 2c259edaa26ab3..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Coordinate } from '../../../../typings/timeseries'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; -import { fetcher } from './fetcher'; -import { transformer } from './transformer'; - -export interface Options { - serviceName: string; - setup: Setup & SetupTimeRange & SetupUIFilters; - searchAggregatedTransactions: boolean; - transactionName?: string; -} - -export type AvgDurationByBrowserAPIResponse = Array<{ - data: Coordinate[]; - title: string; -}>; - -export async function getTransactionAvgDurationByBrowser(options: Options) { - return transformer({ response: await fetcher(options) }); -} diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts deleted file mode 100644 index 91ff2698ea5549..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { transformer } from './transformer'; -import { response } from './__fixtures__/responses'; - -describe('transformer', () => { - it('transforms', () => { - expect(transformer({ response })).toEqual([ - { - data: [ - { x: 1571650700000, y: undefined }, - { x: 1571650800000, y: 86425.1 }, - ], - title: 'Firefox', - }, - { - data: [ - { x: 1571650700000, y: undefined }, - { x: 1571650800000, y: 860425.0 }, - ], - title: 'Other', - }, - ]); - }); -}); diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts deleted file mode 100644 index 5234af8cede667..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_browser/transformer.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ESResponse } from './fetcher'; -import { AvgDurationByBrowserAPIResponse } from '.'; -import { Coordinate } from '../../../../typings/timeseries'; - -export function transformer({ - response, -}: { - response: ESResponse; -}): AvgDurationByBrowserAPIResponse { - const allUserAgentKeys = new Set( - (response.aggregations?.user_agent_keys?.buckets ?? []).map(({ key }) => - key.toString() - ) - ); - const buckets = response.aggregations?.browsers?.buckets ?? []; - - const series = buckets.reduce<{ [key: string]: Coordinate[] }>( - (acc, next) => { - const userAgentBuckets = next.user_agent?.buckets ?? []; - const x = next.key; - const seenUserAgentKeys = new Set(); - - userAgentBuckets.map((userAgentBucket) => { - const key = userAgentBucket.key; - const y = userAgentBucket.avg_duration?.value; - - seenUserAgentKeys.add(key.toString()); - acc[key] = (acc[key] || []).concat({ x, y }); - }); - - const emptyUserAgents = new Set( - [...allUserAgentKeys].filter((key) => !seenUserAgentKeys.has(key)) - ); - - // If no user agent requests exist for this bucked, fill in the data with - // undefined - [...emptyUserAgents].map((key) => { - acc[key] = (acc[key] || []).concat({ x, y: undefined }); - }); - - return acc; - }, - {} - ); - - return Object.entries(series).map(([title, data]) => ({ title, data })); -} diff --git a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts b/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts deleted file mode 100644 index bc1e0af051ace7..00000000000000 --- a/x-pack/plugins/apm/server/lib/transactions/avg_duration_by_country/index.ts +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - CLIENT_GEO_COUNTRY_ISO_CODE, - SERVICE_NAME, - TRANSACTION_TYPE, - TRANSACTION_NAME, -} from '../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; -import { rangeFilter } from '../../../../common/utils/range_filter'; -import { TRANSACTION_PAGE_LOAD } from '../../../../common/transaction_types'; -import { - getProcessorEventForAggregatedTransactions, - getTransactionDurationFieldForAggregatedTransactions, - getDocumentTypeFilterForAggregatedTransactions, -} from '../../helpers/aggregated_transactions'; - -export async function getTransactionAvgDurationByCountry({ - setup, - serviceName, - transactionName, - searchAggregatedTransactions, -}: { - setup: Setup & SetupTimeRange & SetupUIFilters; - serviceName: string; - transactionName?: string; - searchAggregatedTransactions: boolean; -}) { - const { uiFiltersES, apmEventClient, start, end } = setup; - const transactionNameFilter = transactionName - ? [{ term: { [TRANSACTION_NAME]: transactionName } }] - : []; - const params = { - apm: { - events: [ - getProcessorEventForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - body: { - size: 0, - query: { - bool: { - filter: [ - { term: { [SERVICE_NAME]: serviceName } }, - ...transactionNameFilter, - { term: { [TRANSACTION_TYPE]: TRANSACTION_PAGE_LOAD } }, - { exists: { field: CLIENT_GEO_COUNTRY_ISO_CODE } }, - { range: rangeFilter(start, end) }, - ...uiFiltersES, - ...getDocumentTypeFilterForAggregatedTransactions( - searchAggregatedTransactions - ), - ], - }, - }, - aggs: { - country_code: { - terms: { - field: CLIENT_GEO_COUNTRY_ISO_CODE, - size: 500, - }, - aggs: { - count: { - value_count: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - avg_duration: { - avg: { - field: getTransactionDurationFieldForAggregatedTransactions( - searchAggregatedTransactions - ), - }, - }, - }, - }, - }, - }, - }; - - const resp = await apmEventClient.search(params); - - if (!resp.aggregations) { - return []; - } - - const buckets = resp.aggregations.country_code.buckets; - const avgDurationsByCountry = buckets.map( - ({ key, count, avg_duration: { value } }) => ({ - key: key as string, - docCount: count.value, - value: value === null ? 0 : value, - }) - ); - - return avgDurationsByCountry; -} diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index 1230e8aa05c9f7..7d9a9ccc167e0a 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -46,8 +46,6 @@ import { transactionGroupsChartsRoute, transactionGroupsDistributionRoute, transactionGroupsRoute, - transactionGroupsAvgDurationByCountry, - transactionGroupsAvgDurationByBrowser, transactionSampleForGroupRoute, transactionGroupsErrorRateRoute, } from './transaction_groups'; @@ -139,8 +137,6 @@ const createApmApi = () => { .add(transactionGroupsChartsRoute) .add(transactionGroupsDistributionRoute) .add(transactionGroupsRoute) - .add(transactionGroupsAvgDurationByBrowser) - .add(transactionGroupsAvgDurationByCountry) .add(transactionSampleForGroupRoute) .add(transactionGroupsErrorRateRoute) diff --git a/x-pack/plugins/apm/server/routes/transaction_groups.ts b/x-pack/plugins/apm/server/routes/transaction_groups.ts index 3c512c1fe52780..888c4363f77b97 100644 --- a/x-pack/plugins/apm/server/routes/transaction_groups.ts +++ b/x-pack/plugins/apm/server/routes/transaction_groups.ts @@ -12,8 +12,6 @@ import { getTransactionBreakdown } from '../lib/transactions/breakdown'; import { getTransactionGroupList } from '../lib/transaction_groups'; import { createRoute } from './create_route'; import { uiFiltersRt, rangeRt } from './default_api_types'; -import { getTransactionAvgDurationByBrowser } from '../lib/transactions/avg_duration_by_browser'; -import { getTransactionAvgDurationByCountry } from '../lib/transactions/avg_duration_by_country'; import { getTransactionSampleForGroup } from '../lib/transaction_groups/get_transaction_sample_for_group'; import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; import { getErrorRate } from '../lib/transaction_groups/get_error_rate'; @@ -168,68 +166,6 @@ export const transactionGroupsBreakdownRoute = createRoute(() => ({ }, })); -export const transactionGroupsAvgDurationByBrowser = createRoute(() => ({ - path: `/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_browser`, - params: { - path: t.type({ - serviceName: t.string, - }), - query: t.intersection([ - t.partial({ - transactionName: t.string, - }), - uiFiltersRt, - rangeRt, - ]), - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - const { serviceName } = context.params.path; - const { transactionName } = context.params.query; - - const searchAggregatedTransactions = await getSearchAggregatedTransactions( - setup - ); - - return getTransactionAvgDurationByBrowser({ - serviceName, - setup, - searchAggregatedTransactions, - transactionName, - }); - }, -})); - -export const transactionGroupsAvgDurationByCountry = createRoute(() => ({ - path: `/api/apm/services/{serviceName}/transaction_groups/avg_duration_by_country`, - params: { - path: t.type({ - serviceName: t.string, - }), - query: t.intersection([ - uiFiltersRt, - rangeRt, - t.partial({ transactionName: t.string }), - ]), - }, - handler: async ({ context, request }) => { - const setup = await setupRequest(context, request); - const { serviceName } = context.params.path; - const { transactionName } = context.params.query; - - const searchAggregatedTransactions = await getSearchAggregatedTransactions( - setup - ); - - return getTransactionAvgDurationByCountry({ - serviceName, - transactionName, - setup, - searchAggregatedTransactions, - }); - }, -})); - export const transactionSampleForGroupRoute = createRoute(() => ({ path: `/api/apm/transaction_sample`, params: { diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index e0cf2814b46b4e..053c450ab925ef 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -10,7 +10,7 @@ import { AppMountParameters } from 'src/core/public'; import { coreMock } from 'src/core/public/mocks'; import { licensingMock } from '../../../licensing/public/mocks'; -import { renderApp } from './'; +import { renderApp, renderHeaderActions } from './'; import { AppSearch } from './app_search'; import { WorkplaceSearch } from './workplace_search'; @@ -33,6 +33,7 @@ describe('renderApp', () => { const unmount = renderApp(MockApp, params, core, plugins, config, data); expect(params.element.querySelector('.hello-world')).not.toBeNull(); + unmount(); expect(params.element.innerHTML).toEqual(''); }); @@ -47,3 +48,16 @@ describe('renderApp', () => { expect(params.element.querySelector('.setupGuide')).not.toBeNull(); }); }); + +describe('renderHeaderActions', () => { + it('mounts and unmounts any HeaderActions component', () => { + const mockHeaderEl = document.createElement('header'); + const MockHeaderActions = () => ; + + const unmount = renderHeaderActions(MockHeaderActions, mockHeaderEl, {} as any); + expect(mockHeaderEl.querySelector('.hello-world')).not.toBeNull(); + + unmount(); + expect(mockHeaderEl.innerHTML).toEqual(''); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 82f884644be4ad..43056f2f655385 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -88,3 +88,22 @@ export const renderApp = ( ReactDOM.unmountComponentAtNode(params.element); }; }; + +/** + * Render function for Kibana's header action menu chrome - + * reusable by any Enterprise Search plugin simply by passing in + * a custom HeaderActions component (e.g., WorkplaceSearchHeaderActions) + * @see https://github.com/elastic/kibana/blob/master/docs/development/core/public/kibana-plugin-core-public.appmountparameters.setheaderactionmenu.md + */ +interface IHeaderActionsProps { + externalUrl: IExternalUrl; +} + +export const renderHeaderActions = ( + HeaderActions: React.FC, + kibanaHeaderEl: HTMLElement, + externalUrl: IExternalUrl +) => { + ReactDOM.render(, kibanaHeaderEl); + return () => ReactDOM.unmountComponentAtNode(kibanaHeaderEl); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts index 41861a8ee2dc50..915638246c00e8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/index.ts @@ -5,3 +5,4 @@ */ export { WorkplaceSearchNav } from './nav'; +export { WorkplaceSearchHeaderActions } from './kibana_header_actions'; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx new file mode 100644 index 00000000000000..a006c5e3775d52 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.test.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { EuiButtonEmpty } from '@elastic/eui'; +import { ExternalUrl } from '../../../shared/enterprise_search_url'; + +import { WorkplaceSearchHeaderActions } from './'; + +describe('WorkplaceSearchHeaderActions', () => { + const externalUrl = new ExternalUrl('http://localhost:3002'); + + it('renders a link to the search application', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiButtonEmpty).prop('href')).toEqual('http://localhost:3002/ws/search'); + }); + + it('does not render without an Enterprise Search host URL set', () => { + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx new file mode 100644 index 00000000000000..fa32d598f848db --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty } from '@elastic/eui'; + +import { IExternalUrl } from '../../../shared/enterprise_search_url'; + +interface IProps { + externalUrl: IExternalUrl; +} + +export const WorkplaceSearchHeaderActions: React.FC = ({ externalUrl }) => { + const { enterpriseSearchUrl, getWorkplaceSearchUrl } = externalUrl; + if (!enterpriseSearchUrl) return null; + + return ( + + {i18n.translate('xpack.enterpriseSearch.workplaceSearch.headerActions.searchApplication', { + defaultMessage: 'Go to search application', + })} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 0ef58a7c03f10e..c23bb23be3979e 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -103,9 +103,16 @@ export class EnterpriseSearchPlugin implements Plugin { await this.getInitialData(coreStart.http); - const { renderApp } = await import('./applications'); + const { renderApp, renderHeaderActions } = await import('./applications'); const { WorkplaceSearch } = await import('./applications/workplace_search'); + const { WorkplaceSearchHeaderActions } = await import( + './applications/workplace_search/components/layout' + ); + params.setHeaderActionMenu((element) => + renderHeaderActions(WorkplaceSearchHeaderActions, element, this.data.externalUrl) + ); + return renderApp(WorkplaceSearch, params, coreStart, plugins, this.config, this.data); }, }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/point_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/point_datatype.test.tsx new file mode 100644 index 00000000000000..0ee70d63ba6676 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/point_datatype.test.tsx @@ -0,0 +1,158 @@ +/* + * 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 { act } from 'react-dom/test-utils'; + +import { componentHelpers, MappingsEditorTestBed } from '../helpers'; + +const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor; + +// Parameters automatically added to the point datatype when saved (with the default values) +export const defaultPointParameters = { + type: 'point', + ignore_malformed: false, + ignore_z_value: true, +}; + +describe('Mappings editor: point datatype', () => { + /** + * Variable to store the mappings data forwarded to the consumer component + */ + let data: any; + let onChangeHandler: jest.Mock = jest.fn(); + let getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler); + let testBed: MappingsEditorTestBed; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(() => { + onChangeHandler = jest.fn(); + getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler); + }); + + test('initial view and default parameters values', async () => { + const defaultMappings = { + properties: { + myField: { + type: 'point', + }, + }, + }; + + const updatedMappings = { ...defaultMappings }; + + await act(async () => { + testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); + }); + testBed.component.update(); + + const { + component, + actions: { startEditField, updateFieldAndCloseFlyout }, + } = testBed; + + // Open the flyout to edit the field + await startEditField('myField'); + + // Save the field and close the flyout + await updateFieldAndCloseFlyout(); + + // It should have the default parameters values added + updatedMappings.properties.myField = defaultPointParameters; + + ({ data } = await getMappingsEditorData(component)); + expect(data).toEqual(updatedMappings); + }); + + describe('meta parameter', () => { + const defaultMappings = { + properties: { + myField: { + type: 'point', + }, + }, + }; + + const updatedMappings = { ...defaultMappings }; + + const metaParameter = { + meta: { + my_metadata: 'foobar', + }, + }; + + beforeEach(async () => { + await act(async () => { + testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); + }); + testBed.component.update(); + }); + + test('valid meta object', async () => { + const { + component, + actions: { + startEditField, + updateFieldAndCloseFlyout, + showAdvancedSettings, + toggleFormRow, + updateJsonEditor, + }, + } = testBed; + + // Open the flyout to edit the field + await startEditField('myField'); + await showAdvancedSettings(); + + // Enable the meta parameter and add value + toggleFormRow('metaParameter'); + await act(async () => { + updateJsonEditor('metaParameterEditor', metaParameter.meta); + }); + component.update(); + + // Save the field and close the flyout + await updateFieldAndCloseFlyout(); + + // It should have the default parameters values added, plus metadata + updatedMappings.properties.myField = { + ...defaultPointParameters, + ...metaParameter, + }; + + ({ data } = await getMappingsEditorData(component)); + expect(data).toEqual(updatedMappings); + }); + + test('strip empty string', async () => { + const { + component, + actions: { startEditField, updateFieldAndCloseFlyout, showAdvancedSettings, toggleFormRow }, + } = testBed; + + // Open the flyout to edit the field + await startEditField('myField'); + await showAdvancedSettings(); + + // Enable the meta parameter + toggleFormRow('metaParameter'); + + // Save the field and close the flyout without adding any values to meta parameter + await updateFieldAndCloseFlyout(); + + // It should have the default parameters values added + updatedMappings.properties.myField = defaultPointParameters; + + ({ data } = await getMappingsEditorData(component)); + expect(data).toEqual(updatedMappings); + }); + }); +}); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx index 2a4af89c46559f..e123dea6ff2ff6 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.tsx @@ -239,6 +239,10 @@ const createActions = (testBed: TestBed) => { const getCheckboxValue = (testSubject: TestSubjects): boolean => find(testSubject).props().checked; + const toggleFormRow = (formRowName: string) => { + form.toggleEuiSwitch(`${formRowName}.formRowToggle`); + }; + return { selectTab, getFieldAt, @@ -252,6 +256,7 @@ const createActions = (testBed: TestBed) => { getComboBoxValue, getToggleValue, getCheckboxValue, + toggleFormRow, }; }; @@ -365,4 +370,6 @@ export type TestSubjects = | 'searchQuoteAnalyzer-custom' | 'searchQuoteAnalyzer-toggleCustomButton' | 'searchQuoteAnalyzer-custom.input' - | 'useSameAnalyzerForSearchCheckBox.input'; + | 'useSameAnalyzerForSearchCheckBox.input' + | 'metaParameterEditor' + | string; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx index bd118ac08964fb..ce58a264db968c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx @@ -10,15 +10,18 @@ import { i18n } from '@kbn/i18n'; import { EditFieldFormRow } from '../fields/edit_field'; -export const IgnoreZValueParameter = () => ( +export const IgnoreZValueParameter = ({ description }: { description?: string }) => ( ); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/meta_parameter.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/meta_parameter.tsx index c8af296318b61c..a950ba82d0eac4 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/meta_parameter.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/meta_parameter.tsx @@ -32,6 +32,7 @@ export const MetaParameter: FunctionComponent = ({ defaultToggleValue }) }), href: documentationService.getMetaLink(), }} + data-test-subj="metaParameter" > = ({ defaultToggleValue }) component={JsonEditorField} componentProps={{ euiCodeEditorProps: { + ['data-test-subj']: 'metaParameterEditor', height: '300px', 'aria-label': i18n.translate('xpack.idxMgmt.mappingsEditor.metaParameterAriaLabel', { defaultMessage: 'metadata field data editor', diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts index 8fcd02e4a362ea..6b092c5561b3bf 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts @@ -32,6 +32,7 @@ import { HistogramType } from './histogram_type'; import { ConstantKeywordType } from './constant_keyword_type'; import { RankFeatureType } from './rank_feature_type'; import { WildcardType } from './wildcard_type'; +import { PointType } from './point_type'; const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { alias: AliasType, @@ -60,6 +61,7 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { constant_keyword: ConstantKeywordType, rank_feature: RankFeatureType, wildcard: WildcardType, + point: PointType, }; export const getParametersFormForType = ( diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/point_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/point_type.tsx new file mode 100644 index 00000000000000..9108c56e4496b7 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/point_type.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { FunctionComponent } from 'react'; + +import { i18n } from '@kbn/i18n'; + +import { NormalizedField, Field as FieldType, ParameterName } from '../../../../types'; +import { UseField, TextAreaField } from '../../../../shared_imports'; +import { getFieldConfig } from '../../../../lib'; +import { + IgnoreMalformedParameter, + IgnoreZValueParameter, + NullValueParameter, + MetaParameter, +} from '../../field_parameters'; +import { AdvancedParametersSection, BasicParametersSection } from '../edit_field'; + +interface Props { + field: NormalizedField; +} + +const getDefaultToggleValue = (param: ParameterName, field: FieldType) => { + return field[param] !== undefined && field[param] !== getFieldConfig(param).defaultValue; +}; + +export const PointType: FunctionComponent = ({ field }) => { + return ( + <> + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx index a4d3bf3832d5c5..293ae56d57ace5 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx @@ -821,6 +821,26 @@ export const TYPE_DEFINITION: { [key in DataType]: DataTypeDefinition } = {

), }, + point: { + label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.pointDescription', { + defaultMessage: 'Point', + }), + value: 'point', + documentation: { + main: '/point.html', + }, + description: () => ( +

+ {'x,y'}, + }} + /> +

+ ), + }, wildcard: { label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.wildcardDescription', { defaultMessage: 'Wildcard', @@ -882,6 +902,7 @@ export const MAIN_TYPES: MainType[] = [ 'token_count', 'histogram', 'wildcard', + 'point', 'other', ]; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx index fd17dc1b8fd1ef..4ffedc8ca114d9 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx @@ -382,6 +382,50 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio }, schema: t.any, }, + null_value_point: { + fieldConfig: { + defaultValue: '', + label: nullValueLabel, + helpText: () => ( + + {i18n.translate( + 'xpack.idxMgmt.mappingsEditor.parameters.pointWellKnownTextDocumentationLink', + { + defaultMessage: 'Well-Known Text', + } + )} + + ), + }} + /> + ), + validations: [ + { + validator: nullValueValidateEmptyField, + }, + ], + deserializer: (value: any) => { + if (value === '') { + return value; + } + return JSON.stringify(value); + }, + serializer: (value: string) => { + try { + return JSON.parse(value); + } catch (error) { + // swallow error and return non-parsed value; + return value; + } + }, + }, + schema: t.any, + }, copy_to: { fieldConfig: { defaultValue: '', @@ -476,12 +520,22 @@ export const PARAMETERS_DEFINITION: { [key in ParameterName]: ParameterDefinitio return JSON.stringify(value, null, 2); }, serializer: (value: string) => { - const parsed = JSON.parse(value); - // If an empty object was passed, strip out this value entirely. - if (!Object.keys(parsed).length) { + // Strip out empty strings + if (value.trim() === '') { return undefined; } - return parsed; + + try { + const parsed = JSON.parse(value); + // If an empty object was passed, strip out this value entirely. + if (!Object.keys(parsed).length) { + return undefined; + } + return parsed; + } catch (error) { + // swallow error and return non-parsed value; + return value; + } }, }, schema: t.any, diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts index 97dca49fc93ed5..ca38a8d1e6c33c 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts @@ -59,6 +59,7 @@ export type MainType = | 'geo_point' | 'geo_shape' | 'token_count' + | 'point' | 'histogram' | 'constant_keyword' | 'wildcard' @@ -109,6 +110,7 @@ export type ParameterName = | 'null_value_boolean' | 'null_value_geo_point' | 'null_value_ip' + | 'null_value_point' | 'copy_to' | 'dynamic' | 'dynamic_toggle' diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/components/layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/components/layout.tsx index 3bcf0aab9a5c83..6edce74d162bbb 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/components/layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/components/layout.tsx @@ -111,7 +111,7 @@ export const CreatePackagePolicyPageLayout: React.FunctionComponent<{ ) : ( ); }, [from]); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/index.tsx index a02214a6fe7faa..39f35fed56ef51 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/index.tsx @@ -242,7 +242,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { notifications.toasts.addSuccess({ title: i18n.translate('xpack.ingestManager.createPackagePolicy.addedNotificationTitle', { - defaultMessage: `Successfully added '{packagePolicyName}'`, + defaultMessage: `'{packagePolicyName}' integration added.`, values: { packagePolicyName: packagePolicy.name, }, @@ -250,7 +250,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => { text: agentCount && agentPolicy ? i18n.translate('xpack.ingestManager.createPackagePolicy.addedNotificationMessage', { - defaultMessage: `Fleet will deploy updates to all agents that use the '{agentPolicyName}' policy`, + defaultMessage: `Fleet will deploy updates to all agents that use the '{agentPolicyName}' policy.`, values: { agentPolicyName: agentPolicy.name, }, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/components/confirm_delete_modal.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/components/confirm_delete_modal.tsx index d2092f070a22ad..a115e03a369a26 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/components/confirm_delete_modal.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/components/confirm_delete_modal.tsx @@ -21,7 +21,7 @@ export const ConfirmEnrollmentTokenDelete = (props: Props) => { { confirmButtonText={i18n.translate( 'xpack.ingestManager.enrollmentTokenDeleteModal.deleteButton', { - defaultMessage: 'Delete', + defaultMessage: 'Revoke enrollment token', } )} defaultFocusedButton="confirm" @@ -42,7 +42,8 @@ export const ConfirmEnrollmentTokenDelete = (props: Props) => { > = ({

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx index d85a6e8b5b833e..f447469a02df2e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx @@ -268,7 +268,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { @@ -290,7 +290,7 @@ export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { setFlyoutOpen(true)}> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx index eeade9036df000..fbd74f8b03e72d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx @@ -95,8 +95,7 @@ export const SetupPage: React.FunctionComponent<{ diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts index b0439b30e8973b..fb320b01dea975 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/acks_handlers.ts @@ -10,6 +10,7 @@ import { RequestHandler } from 'kibana/server'; import { AcksService } from '../../services/agents'; import { AgentEvent } from '../../../common/types/models'; import { PostAgentAcksRequest, PostAgentAcksResponse } from '../../../common/types/rest_spec'; +import { defaultIngestErrorHandler } from '../../errors'; export const postAgentAcksHandlerBuilder = function ( ackService: AcksService @@ -43,18 +44,8 @@ export const postAgentAcksHandlerBuilder = function ( }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index 12a0956b791550..64a7795cc9dacd 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -11,6 +11,7 @@ import { TypeOf } from '@kbn/config-schema'; import { PostNewAgentActionRequestSchema } from '../../types/rest_spec'; import { ActionsService } from '../../services/agents'; import { PostNewAgentActionResponse } from '../../../common/types/rest_spec'; +import { defaultIngestErrorHandler } from '../../errors'; export const postNewAgentActionHandlerBuilder = function ( actionsService: ActionsService @@ -38,18 +39,8 @@ export const postNewAgentActionHandlerBuilder = function ( }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index b9789b770eb2e2..2ebb7a0667aab1 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -47,17 +47,14 @@ export const getAgentHandler: RequestHandler= 500) { - logger.error(err); - } - - return response.customError({ - statusCode: err.output.statusCode, - body: { message: err.output.payload.message }, - }); - } - - logger.error(err); - - return response.customError({ - statusCode: 500, - body: { message: err.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -247,18 +221,8 @@ export const postAgentEnrollHandler: RequestHandler< }; return response.ok({ body }); - } catch (e) { - if (e.isBoom) { - return response.customError({ - statusCode: e.output.statusCode, - body: { message: e.message }, - }); - } - - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -301,11 +265,8 @@ export const putAgentsReassignHandler: RequestHandler< const body: PutAgentReassignResponse = {}; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -324,10 +285,7 @@ export const getAgentStatusForAgentPolicyHandler: RequestHandler< const body: GetAgentStatusResponse = { results }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts index 5df695d248f5b6..fa200e912d6250 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/unenroll_handler.ts @@ -9,6 +9,7 @@ import { TypeOf } from '@kbn/config-schema'; import { PostAgentUnenrollResponse } from '../../../common/types'; import { PostAgentUnenrollRequestSchema } from '../../types'; import * as AgentService from '../../services/agents'; +import { defaultIngestErrorHandler } from '../../errors'; export const postAgentsUnenrollHandler: RequestHandler< TypeOf, @@ -25,10 +26,7 @@ export const postAgentsUnenrollHandler: RequestHandler< const body: PostAgentUnenrollResponse = {}; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts index 7eb3df23461068..311b3bbf7f13ba 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_policy/handlers.ts @@ -32,6 +32,7 @@ import { DeleteAgentPolicyResponse, GetFullAgentPolicyResponse, } from '../../../common'; +import { defaultIngestErrorHandler } from '../../errors'; export const getAgentPoliciesHandler: RequestHandler< undefined, @@ -64,11 +65,8 @@ export const getAgentPoliciesHandler: RequestHandler< ); return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -91,11 +89,8 @@ export const getOneAgentPolicyHandler: RequestHandler { const soClient = context.core.savedObjects.client; @@ -23,11 +24,8 @@ export const getOutputsHandler: RequestHandler = async (context, request, respon }; return response.ok({ body }); - } catch (e) { - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + } catch (error) { + return defaultIngestErrorHandler({ error, response }); } }; @@ -43,17 +41,14 @@ export const getOneOuputHandler: RequestHandler { @@ -19,17 +19,14 @@ export const getSettingsHandler: RequestHandler = async (context, request, respo item: settings, }; return response.ok({ body }); - } catch (e) { - if (e.isBoom && e.output.statusCode === 404) { + } catch (error) { + if (error.isBoom && error.output.statusCode === 404) { return response.notFound({ body: { message: `Setings not found` }, }); } - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + return defaultIngestErrorHandler({ error, response }); } }; @@ -49,17 +46,14 @@ export const putSettingsHandler: RequestHandler< item: settings, }; return response.ok({ body }); - } catch (e) { - if (e.isBoom && e.output.statusCode === 404) { + } catch (error) { + if (error.isBoom && error.output.statusCode === 404) { return response.notFound({ body: { message: `Setings not found` }, }); } - return response.customError({ - statusCode: 500, - body: { message: e.message }, - }); + return defaultIngestErrorHandler({ error, response }); } }; diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts index 3de57b085a9c67..fdfa042e8fcc98 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts.spec.ts @@ -30,7 +30,8 @@ import { loginAndWaitForPage } from '../tasks/login'; import { DETECTIONS_URL } from '../urls/navigation'; -describe('Alerts', () => { +// FLAKY: https://github.com/elastic/kibana/issues/77957 +describe.skip('Alerts', () => { context('Closing alerts', () => { beforeEach(() => { esArchiverLoad('alerts'); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts index 4faef85afbdc8d..61bcd222b1b1ee 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/index.test.ts @@ -58,6 +58,7 @@ describe('EndpointList store concerns', () => { patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, + queryStrategyVersion: undefined, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts index 7673702f543702..7872c8824a8eef 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.ts @@ -17,6 +17,7 @@ import { nonExistingPolicies, patterns, searchBarQuery, + isTransformEnabled, } from './selectors'; import { EndpointState, PolicyIds } from '../types'; import { @@ -70,24 +71,6 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory HostResultList = (options = {}) => { const { total = 1, request_page_size: requestPageSize = 10, request_page_index: requestPageIndex = 0, + query_strategy_version: queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2, } = options; // Skip any that are before the page we're on @@ -50,7 +52,7 @@ export const mockEndpointResultList: (options?: { hosts.push({ metadata: generator.generateHostMetadata(), host_status: HostStatus.ERROR, - query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, + query_strategy_version: queryStrategyVersion, }); } const mock: HostResultList = { @@ -58,7 +60,7 @@ export const mockEndpointResultList: (options?: { total, request_page_size: requestPageSize, request_page_index: requestPageIndex, - query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, + query_strategy_version: queryStrategyVersion, }; return mock; }; @@ -84,6 +86,7 @@ const endpointListApiPathHandlerMocks = ({ endpointPackagePolicies = [], policyResponse = generator.generatePolicyResponse(), agentPolicy = generator.generateAgentPolicy(), + queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2, }: { /** route handlers will be setup for each individual host in this array */ endpointsResults?: HostResultList['hosts']; @@ -91,6 +94,7 @@ const endpointListApiPathHandlerMocks = ({ endpointPackagePolicies?: GetPolicyListResponse['items']; policyResponse?: HostPolicyResponse; agentPolicy?: GetAgentPoliciesResponseItem; + queryStrategyVersion?: MetadataQueryStrategyVersions; } = {}) => { const apiHandlers = { // endpoint package info @@ -107,7 +111,7 @@ const endpointListApiPathHandlerMocks = ({ request_page_size: 10, request_page_index: 0, total: endpointsResults?.length || 0, - query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, + query_strategy_version: queryStrategyVersion, }; }, @@ -164,11 +168,16 @@ export const setEndpointListApiMockImplementation: ( apiResponses?: Parameters[0] ) => void = ( mockedHttpService, - { endpointsResults = mockEndpointResultList({ total: 3 }).hosts, ...pathHandlersOptions } = {} + { + endpointsResults = mockEndpointResultList({ total: 3 }).hosts, + queryStrategyVersion = MetadataQueryStrategyVersions.VERSION_2, + ...pathHandlersOptions + } = {} ) => { const apiHandlers = endpointListApiPathHandlerMocks({ ...pathHandlersOptions, endpointsResults, + queryStrategyVersion, }); mockedHttpService.post diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts index 99a1df7eb40024..0f948f74a48e4b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts @@ -36,6 +36,7 @@ export const initialEndpointListState: Immutable = { patternsError: undefined, isAutoRefreshEnabled: true, autoRefreshInterval: DEFAULT_POLL_INTERVAL, + queryStrategyVersion: undefined, }; /* eslint-disable-next-line complexity */ @@ -49,6 +50,7 @@ export const endpointListReducer: ImmutableReducer = ( total, request_page_size: pageSize, request_page_index: pageIndex, + query_strategy_version: queryStrategyVersion, } = action.payload; return { ...state, @@ -56,6 +58,7 @@ export const endpointListReducer: ImmutableReducer = ( total, pageSize, pageIndex, + queryStrategyVersion, loading: false, error: undefined, }; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts index a3eee8063d6f7e..fe47d60afc339e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/selectors.ts @@ -14,6 +14,7 @@ import { HostPolicyResponseAppliedAction, HostPolicyResponseConfiguration, HostPolicyResponseActionStatus, + MetadataQueryStrategyVersions, } from '../../../../../common/endpoint/types'; import { EndpointState, EndpointIndexUIQueryParams } from '../types'; import { extractListPaginationParams } from '../../../common/routing'; @@ -54,11 +55,18 @@ export const isAutoRefreshEnabled = (state: Immutable) => state.i export const autoRefreshInterval = (state: Immutable) => state.autoRefreshInterval; +const queryStrategyVersion = (state: Immutable) => state.queryStrategyVersion; + export const endpointPackageVersion = createSelector( endpointPackageInfo, (info) => info?.version ?? undefined ); +export const isTransformEnabled = createSelector( + queryStrategyVersion, + (version) => version !== MetadataQueryStrategyVersions.VERSION_1 +); + /** * Returns the index patterns for the SearchBar to use for autosuggest */ diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts index 77f21243ea120d..bdd0d5e942cef2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/types.ts @@ -11,6 +11,7 @@ import { HostPolicyResponse, AppLocation, PolicyData, + MetadataQueryStrategyVersions, } from '../../../../common/endpoint/types'; import { ServerApiError } from '../../../common/types'; import { GetPackagesResponse } from '../../../../../ingest_manager/common'; @@ -65,6 +66,8 @@ export interface EndpointState { isAutoRefreshEnabled: boolean; /** The current auto refresh interval for data in ms */ autoRefreshInterval: number; + /** The query strategy version that informs whether the transform for KQL is enabled or not */ + queryStrategyVersion?: MetadataQueryStrategyVersions; } /** diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index bd8344f41fe3ac..51a6be18471aa9 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -108,6 +108,31 @@ describe('when on the list page', () => { }); }); + describe('when loading data with the query_strategy_version is `v1`', () => { + beforeEach(() => { + reactTestingLibrary.act(() => { + const mockedEndpointListData = mockEndpointResultList({ + total: 4, + query_strategy_version: MetadataQueryStrategyVersions.VERSION_1, + }); + setEndpointListApiMockImplementation(coreStart.http, { + endpointsResults: mockedEndpointListData.hosts, + queryStrategyVersion: mockedEndpointListData.query_strategy_version, + }); + }); + }); + afterEach(() => { + jest.clearAllMocks(); + }); + it('should not display the KQL bar', async () => { + const renderResult = render(); + await reactTestingLibrary.act(async () => { + await middlewareSpy.waitForAction('serverReturnedEndpointList'); + }); + expect(renderResult.queryByTestId('adminSearchBar')).toBeNull(); + }); + }); + describe('when there is no selected host in the url', () => { it('should not show the flyout', () => { const renderResult = render(); @@ -123,7 +148,9 @@ describe('when on the list page', () => { let firstPolicyID: string; beforeEach(() => { reactTestingLibrary.act(() => { - const hostListData = mockEndpointResultList({ total: 4 }).hosts; + const mockedEndpointData = mockEndpointResultList({ total: 4 }); + const hostListData = mockedEndpointData.hosts; + const queryStrategyVersion = mockedEndpointData.query_strategy_version; firstPolicyID = hostListData[0].metadata.Endpoint.policy.applied.id; @@ -132,7 +159,7 @@ describe('when on the list page', () => { hostListData[index] = { metadata: hostListData[index].metadata, host_status: status, - query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, + query_strategy_version: queryStrategyVersion, }; } ); @@ -682,11 +709,11 @@ describe('when on the list page', () => { let renderAndWaitForData: () => Promise>; const mockEndpointListApi = () => { - const { hosts } = mockEndpointResultList(); + const { hosts, query_strategy_version: queryStrategyVersion } = mockEndpointResultList(); hostInfo = { host_status: hosts[0].host_status, metadata: hosts[0].metadata, - query_strategy_version: MetadataQueryStrategyVersions.VERSION_2, + query_strategy_version: queryStrategyVersion, }; const packagePolicy = docGenerator.generatePolicyPackagePolicy(); packagePolicy.id = hosts[0].metadata.Endpoint.policy.applied.id; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index ecee2bee0c58b4..3e1f08eee7b947 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -135,6 +135,7 @@ export const EndpointList = () => { autoRefreshInterval, isAutoRefreshEnabled, patternsError, + isTransformEnabled, } = useEndpointSelector(selector); const { formatUrl, search } = useFormatUrl(SecurityPageName.administration); @@ -532,8 +533,8 @@ export const EndpointList = () => { const hasListData = listData && listData.length > 0; const refreshStyle = useMemo(() => { - return { display: endpointsExist ? 'flex' : 'none', maxWidth: 200 }; - }, [endpointsExist]); + return { display: endpointsExist && isTransformEnabled ? 'flex' : 'none', maxWidth: 200 }; + }, [endpointsExist, isTransformEnabled]); const refreshIsPaused = useMemo(() => { return !endpointsExist ? false : hasSelectedEndpoint ? true : !isAutoRefreshEnabled; @@ -543,6 +544,10 @@ export const EndpointList = () => { return !endpointsExist ? DEFAULT_POLL_INTERVAL : autoRefreshInterval; }, [endpointsExist, autoRefreshInterval]); + const shouldShowKQLBar = useMemo(() => { + return endpointsExist && !patternsError && isTransformEnabled; + }, [endpointsExist, patternsError, isTransformEnabled]); + return ( { {hasSelectedEndpoint && } <> - {endpointsExist && !patternsError && ( + {shouldShowKQLBar && ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e50225ceaaa133..dee03bf5ff8061 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4756,10 +4756,6 @@ "xpack.apm.metadataTable.section.urlLabel": "URL", "xpack.apm.metadataTable.section.userAgentLabel": "ユーザーエージェント", "xpack.apm.metadataTable.section.userLabel": "ユーザー", - "xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel": "国ごとの平均ページ読み込み時間の分布", - "xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.avgPageLoadDuration": "平均ページ読み込み時間:", - "xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.countPageLoads": "{docCount} ページの読み込み", - "xpack.apm.metrics.pageLoadCharts.avgPageLoadByBrowser": "ブラウザごとの平均ページ読み込み時間の分布", "xpack.apm.metrics.plot.noDataLabel": "この時間範囲のデータがありません。", "xpack.apm.metrics.transactionChart.machineLearningLabel": "機械学習:", "xpack.apm.metrics.transactionChart.machineLearningTooltip": "平均期間の周りのストリームには予測バウンドが表示されます。異常スコアが>= 75の場合、注釈が表示されます。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b765f09fa4eccc..55598306209e79 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4758,10 +4758,6 @@ "xpack.apm.metadataTable.section.urlLabel": "URL", "xpack.apm.metadataTable.section.userAgentLabel": "用户代理", "xpack.apm.metadataTable.section.userLabel": "用户", - "xpack.apm.metrics.durationByCountryMap.avgPageLoadByCountryLabel": "页面加载平均时长分布(按国家/地区)", - "xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.avgPageLoadDuration": "页面加载平均时长:", - "xpack.apm.metrics.durationByCountryMap.RegionMapChart.ToolTip.countPageLoads": "{docCount} 个页面加载", - "xpack.apm.metrics.pageLoadCharts.avgPageLoadByBrowser": "平均页面加载持续时间分布 - 按浏览器", "xpack.apm.metrics.plot.noDataLabel": "此时间范围内没有数据。", "xpack.apm.metrics.transactionChart.machineLearningLabel": "Machine Learning", "xpack.apm.metrics.transactionChart.machineLearningTooltip": "环绕平均持续时间的流显示预期边界。对 ≥ 75 的异常分数显示标注。", diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts index 8aa509b0899ceb..9e1cb1f5872f10 100644 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ b/x-pack/test/apm_api_integration/basic/tests/index.ts @@ -45,7 +45,6 @@ export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderCont loadTestFile(require.resolve('./transaction_groups/transaction_charts')); loadTestFile(require.resolve('./transaction_groups/error_rate')); loadTestFile(require.resolve('./transaction_groups/breakdown')); - loadTestFile(require.resolve('./transaction_groups/avg_duration_by_browser')); }); describe('Observability overview', function () { diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap deleted file mode 100644 index ab7b71cdf9e53b..00000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/__snapshots__/avg_duration_by_browser.snap +++ /dev/null @@ -1,438 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Average duration by browser when data is loaded returns the average duration by browser 1`] = ` -Array [ - Object { - "data": Array [ - Object { - "x": 1600159980000, - "y": 1131000, - }, - Object { - "x": 1600160010000, - "y": 617000, - }, - Object { - "x": 1600160040000, - }, - Object { - "x": 1600160070000, - "y": 1963000, - }, - Object { - "x": 1600160100000, - "y": 1190000, - }, - Object { - "x": 1600160130000, - "y": 1697000, - }, - Object { - "x": 1600160160000, - }, - Object { - "x": 1600160190000, - "y": 710000, - }, - Object { - "x": 1600160220000, - "y": 675250, - }, - Object { - "x": 1600160250000, - }, - Object { - "x": 1600160280000, - }, - Object { - "x": 1600160310000, - "y": 1763500, - }, - Object { - "x": 1600160340000, - "y": 539333.3333333334, - }, - Object { - "x": 1600160370000, - }, - Object { - "x": 1600160400000, - }, - Object { - "x": 1600160430000, - "y": 1650000, - }, - Object { - "x": 1600160460000, - "y": 781000, - }, - Object { - "x": 1600160490000, - }, - Object { - "x": 1600160520000, - }, - Object { - "x": 1600160550000, - "y": 1710000, - }, - Object { - "x": 1600160580000, - "y": 718000, - }, - Object { - "x": 1600160610000, - "y": 11912000, - }, - Object { - "x": 1600160640000, - }, - Object { - "x": 1600160670000, - "y": 885000, - }, - Object { - "x": 1600160700000, - "y": 1043000, - }, - Object { - "x": 1600160730000, - "y": 406000, - }, - Object { - "x": 1600160760000, - }, - Object { - "x": 1600160790000, - "y": 1296000, - }, - Object { - "x": 1600160820000, - "y": 570500, - }, - Object { - "x": 1600160850000, - }, - Object { - "x": 1600160880000, - }, - Object { - "x": 1600160910000, - "y": 1110000, - }, - Object { - "x": 1600160940000, - "y": 533500, - }, - Object { - "x": 1600160970000, - "y": 782500, - }, - Object { - "x": 1600161000000, - }, - Object { - "x": 1600161030000, - "y": 1200000, - }, - Object { - "x": 1600161060000, - "y": 522000, - }, - Object { - "x": 1600161090000, - }, - Object { - "x": 1600161120000, - }, - Object { - "x": 1600161150000, - "y": 1006000, - }, - Object { - "x": 1600161180000, - "y": 1203000, - }, - Object { - "x": 1600161210000, - }, - Object { - "x": 1600161240000, - }, - Object { - "x": 1600161270000, - "y": 1908000, - }, - Object { - "x": 1600161300000, - "y": 549000, - }, - Object { - "x": 1600161330000, - "y": 685000, - }, - Object { - "x": 1600161360000, - }, - Object { - "x": 1600161390000, - "y": 2055000, - }, - Object { - "x": 1600161420000, - "y": 699750, - }, - Object { - "x": 1600161450000, - }, - Object { - "x": 1600161480000, - }, - Object { - "x": 1600161510000, - "y": 1557000, - }, - Object { - "x": 1600161540000, - "y": 4993000, - }, - Object { - "x": 1600161570000, - }, - Object { - "x": 1600161600000, - }, - Object { - "x": 1600161630000, - "y": 898000, - }, - Object { - "x": 1600161660000, - "y": 4100500, - }, - Object { - "x": 1600161690000, - }, - Object { - "x": 1600161720000, - }, - Object { - "x": 1600161750000, - "y": 1305000, - }, - Object { - "x": 1600161780000, - }, - ], - "title": "Electron", - }, -] -`; - -exports[`Average duration by browser when data is loaded returns the average duration by browser filtering by transaction name 2`] = ` -Array [ - Object { - "data": Array [ - Object { - "x": 1600159980000, - }, - Object { - "x": 1600160010000, - }, - Object { - "x": 1600160040000, - }, - Object { - "x": 1600160070000, - "y": 1096000, - }, - Object { - "x": 1600160100000, - }, - Object { - "x": 1600160130000, - }, - Object { - "x": 1600160160000, - }, - Object { - "x": 1600160190000, - "y": 710000, - }, - Object { - "x": 1600160220000, - }, - Object { - "x": 1600160250000, - }, - Object { - "x": 1600160280000, - }, - Object { - "x": 1600160310000, - "y": 1108000, - }, - Object { - "x": 1600160340000, - }, - Object { - "x": 1600160370000, - }, - Object { - "x": 1600160400000, - }, - Object { - "x": 1600160430000, - "y": 1221000, - }, - Object { - "x": 1600160460000, - }, - Object { - "x": 1600160490000, - }, - Object { - "x": 1600160520000, - }, - Object { - "x": 1600160550000, - "y": 1325000, - }, - Object { - "x": 1600160580000, - }, - Object { - "x": 1600160610000, - }, - Object { - "x": 1600160640000, - }, - Object { - "x": 1600160670000, - "y": 885000, - }, - Object { - "x": 1600160700000, - }, - Object { - "x": 1600160730000, - }, - Object { - "x": 1600160760000, - }, - Object { - "x": 1600160790000, - "y": 1296000, - }, - Object { - "x": 1600160820000, - }, - Object { - "x": 1600160850000, - }, - Object { - "x": 1600160880000, - }, - Object { - "x": 1600160910000, - "y": 1110000, - }, - Object { - "x": 1600160940000, - }, - Object { - "x": 1600160970000, - }, - Object { - "x": 1600161000000, - }, - Object { - "x": 1600161030000, - "y": 1200000, - }, - Object { - "x": 1600161060000, - }, - Object { - "x": 1600161090000, - }, - Object { - "x": 1600161120000, - }, - Object { - "x": 1600161150000, - "y": 1006000, - }, - Object { - "x": 1600161180000, - }, - Object { - "x": 1600161210000, - }, - Object { - "x": 1600161240000, - }, - Object { - "x": 1600161270000, - "y": 1908000, - }, - Object { - "x": 1600161300000, - }, - Object { - "x": 1600161330000, - }, - Object { - "x": 1600161360000, - }, - Object { - "x": 1600161390000, - "y": 1420000, - }, - Object { - "x": 1600161420000, - }, - Object { - "x": 1600161450000, - }, - Object { - "x": 1600161480000, - }, - Object { - "x": 1600161510000, - "y": 1215000, - }, - Object { - "x": 1600161540000, - }, - Object { - "x": 1600161570000, - }, - Object { - "x": 1600161600000, - }, - Object { - "x": 1600161630000, - "y": 898000, - }, - Object { - "x": 1600161660000, - }, - Object { - "x": 1600161690000, - }, - Object { - "x": 1600161720000, - }, - Object { - "x": 1600161750000, - "y": 1305000, - }, - Object { - "x": 1600161780000, - }, - ], - "title": "Electron", - }, -] -`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts deleted file mode 100644 index 087bf1f0655e69..00000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/avg_duration_by_browser.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import archives_metadata from '../../../common/archives_metadata'; -import { expectSnapshot } from '../../../common/match_snapshot'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - const metadata = archives_metadata[archiveName]; - - const start = encodeURIComponent(metadata.start); - const end = encodeURIComponent(metadata.end); - const transactionName = '/products'; - const uiFilters = encodeURIComponent(JSON.stringify({})); - - describe('Average duration by browser', () => { - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services/client/transaction_groups/avg_duration_by_browser?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - expect(response.status).to.be(200); - expect(response.body).to.eql([]); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - it('returns the average duration by browser', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-rum/transaction_groups/avg_duration_by_browser?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - - expect(response.status).to.be(200); - - expect(response.body.length).to.be.greaterThan(0); - - expectSnapshot(response.body).toMatch(); - - expectSnapshot(response.body.length).toMatchInline(`1`); - }); - - it('returns the average duration by browser filtering by transaction name', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-rum/transaction_groups/avg_duration_by_browser?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionName=${transactionName}` - ); - - expect(response.status).to.be(200); - - expect(response.body.length).to.be.greaterThan(0); - - expectSnapshot(response.body.length).toMatchInline(`1`); - - expectSnapshot(response.body).toMatch(); - }); - }); - }); -} diff --git a/x-pack/test/functional/apps/discover/error_handling.ts b/x-pack/test/functional/apps/discover/error_handling.ts new file mode 100644 index 00000000000000..515e5e293ae280 --- /dev/null +++ b/x-pack/test/functional/apps/discover/error_handling.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + + describe('errors', function describeIndexTests() { + before(async function () { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.load('invalid_scripted_field'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + }); + + after(async function () { + await esArchiver.unload('invalid_scripted_field'); + }); + // this is the same test as in OSS but it catches different error message issue in different licences + describe('invalid scripted field error', () => { + it('is rendered', async () => { + const isFetchErrorVisible = await testSubjects.exists('discoverFetchError'); + expect(isFetchErrorVisible).to.be(true); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/discover/index.ts b/x-pack/test/functional/apps/discover/index.ts index 323b728e164544..759225d80fa206 100644 --- a/x-pack/test/functional/apps/discover/index.ts +++ b/x-pack/test/functional/apps/discover/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./preserve_url')); loadTestFile(require.resolve('./async_scripted_fields')); loadTestFile(require.resolve('./reporting')); + loadTestFile(require.resolve('./error_handling')); }); } diff --git a/x-pack/test/functional/es_archives/invalid_scripted_field/data.json.gz b/x-pack/test/functional/es_archives/invalid_scripted_field/data.json.gz new file mode 100644 index 00000000000000..380dd6049179a5 Binary files /dev/null and b/x-pack/test/functional/es_archives/invalid_scripted_field/data.json.gz differ diff --git a/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json new file mode 100644 index 00000000000000..0024c6943ed1c0 --- /dev/null +++ b/x-pack/test/functional/es_archives/invalid_scripted_field/mappings.json @@ -0,0 +1,250 @@ +{ + "type": "index", + "value": { + "index": ".kibana", + "mappings": { + "dynamic": "strict", + "properties": { + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + }, + "defaultIndex": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchId": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file diff --git a/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts index 410b0fe0930024..71e2bbc4e4c0a0 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/agent_policy/agent_policy.ts @@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { }); }); - it('should return a 500 with invalid source policy', async () => { + it('should return a 404 with invalid source policy', async () => { await supertest .post(`/api/ingest_manager/agent_policies/INVALID_POLICY_ID/copy`) .set('kbn-xsrf', 'xxxx') @@ -90,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) { name: 'Copied policy', description: '', }) - .expect(500); + .expect(404); }); it('should return a 400 with invalid payload', async () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 325283f5e3440f..9610144d3846df 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -29,7 +29,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { port, }); - describe('When on the Endpoint Policy Details Page', function () { + // FAILING ES PROMOTION: https://github.com/elastic/kibana/issues/72102 + describe.skip('When on the Endpoint Policy Details Page', function () { this.tags(['ciGroup7']); describe('with an invalid policy id', () => {