From 349fd5fcade9e6c80bbe3be1b13e50fa6efbc24a Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Mon, 25 Jan 2021 15:26:53 +0300 Subject: [PATCH 01/90] Advanced JSON input functional test (#88609) * Advanced JSON input functional test * Replace string check with object property check Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/visualize/_inspector.ts | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/functional/apps/visualize/_inspector.ts b/test/functional/apps/visualize/_inspector.ts index 07b5dfb8a769d..9d4623feef74a 100644 --- a/test/functional/apps/visualize/_inspector.ts +++ b/test/functional/apps/visualize/_inspector.ts @@ -6,12 +6,15 @@ * Public License, v 1. */ +import expect from '@kbn/expect'; + import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const inspector = getService('inspector'); const filterBar = getService('filterBar'); + const testSubjects = getService('testSubjects'); const PageObjects = getPageObjects(['visualize', 'visEditor', 'visChart', 'timePicker']); describe('inspector', function describeIndexTests() { @@ -23,6 +26,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.timePicker.setDefaultAbsoluteRange(); }); + describe('advanced input JSON', () => { + it('should have "missing" property with value 10', async () => { + log.debug('Add Max Metric on memory field'); + await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); + await PageObjects.visEditor.selectAggregation('Max', 'metrics'); + await PageObjects.visEditor.selectField('memory', 'metrics'); + + log.debug('Add value to advanced JSON input'); + await PageObjects.visEditor.toggleAdvancedParams('2'); + await testSubjects.setValue('codeEditorContainer', '{ "missing": 10 }'); + await PageObjects.visEditor.clickGo(); + + await inspector.open(); + await inspector.openInspectorRequestsView(); + const requestTab = await inspector.getOpenRequestDetailRequestButton(); + await requestTab.click(); + const requestJSON = JSON.parse(await inspector.getCodeEditorValue()); + + expect(requestJSON.aggs['2'].max).property('missing', 10); + }); + + after(async () => { + await inspector.close(); + await PageObjects.visEditor.removeDimension(2); + await PageObjects.visEditor.clickGo(); + }); + }); + describe('inspector table', function indexPatternCreation() { it('should update table header when columns change', async function () { await inspector.open(); From 191ad14ac2ec5e275750b9d93db72c71a1d7ef98 Mon Sep 17 00:00:00 2001 From: Diana Derevyankina <54894989+DziyanaDzeraviankina@users.noreply.github.com> Date: Mon, 25 Jan 2021 15:47:03 +0300 Subject: [PATCH 02/90] Small multiples in vis_type_xy plugin (#86880) * Small multiples in vis_type_xy plugin * Fix tooltip and formatted split chart values * update advanced settings wording * Remove React import in files with no JSX and change the extension to .ts * Simplify conditions * fix bar interval on split charts in vislib * Fix charts not splitting for terms boolean fields * fix filtering for small multiples * Change tests interval values from 100 to 1000000 * Revert "Change tests interval values from 100 to 1000000" This reverts commit 92f9d1b4b9e0b1f3a6c6d1058c36c657d2c5c68f. * Fix tests for interval issue in vislib (cherry picked from commit ef45b63c47da403399f76f00b49329531d445f31) * Revert axis_scale changes related to interval * Enable _line_chart_split_chart test for new charts library * Move chart splitter id to const Co-authored-by: nickofthyme Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/management/advanced-options.asciidoc | 2 +- .../static/utils/transform_click_event.ts | 62 ++++++++++++++++--- .../public/vislib/lib/axis/axis_scale.js | 10 +-- .../vis_type_xy/public/chart_splitter.tsx | 45 ++++++++++++++ .../public/components/detailed_tooltip.tsx | 34 +++++++--- .../vis_type_xy/public/components/index.ts | 1 - .../public/components/split_chart_warning.tsx | 44 ------------- .../vis_type_xy/public/config/get_aspects.ts | 7 ++- .../vis_type_xy/public/types/config.ts | 2 + .../vis_type_xy/public/utils/accessors.tsx | 15 +++-- .../vis_type_xy/public/vis_component.tsx | 40 ++++++++++-- .../vis_type_xy/public/vis_renderer.tsx | 25 +++----- .../public/vis_types/{area.tsx => area.ts} | 9 --- .../vis_types/{histogram.tsx => histogram.ts} | 9 --- .../{horizontal_bar.tsx => horizontal_bar.ts} | 9 --- .../public/vis_types/{line.tsx => line.ts} | 9 --- .../public/vis_types/split_tooltip.tsx | 20 ------ src/plugins/vis_type_xy/server/plugin.ts | 3 +- .../apps/visualize/_line_chart_split_chart.ts | 28 ++++++--- test/functional/apps/visualize/index.ts | 1 + 20 files changed, 213 insertions(+), 162 deletions(-) create mode 100644 src/plugins/vis_type_xy/public/chart_splitter.tsx delete mode 100644 src/plugins/vis_type_xy/public/components/split_chart_warning.tsx rename src/plugins/vis_type_xy/public/vis_types/{area.tsx => area.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{histogram.tsx => histogram.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{horizontal_bar.tsx => horizontal_bar.ts} (94%) rename src/plugins/vis_type_xy/public/vis_types/{line.tsx => line.ts} (94%) delete mode 100644 src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 7e7c8953fd527..c2306b80734d8 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -458,7 +458,7 @@ of buckets to try to represent. [horizontal] [[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`:: -Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation. +Enables legacy charts library for area, line and bar charts in visualize. [[visualization-colormapping]]`visualization:colorMapping`:: **This setting is deprecated and will not be supported as of 8.0.** diff --git a/src/plugins/charts/public/static/utils/transform_click_event.ts b/src/plugins/charts/public/static/utils/transform_click_event.ts index 20865bea2f897..7c28db333cc83 100644 --- a/src/plugins/charts/public/static/utils/transform_click_event.ts +++ b/src/plugins/charts/public/static/utils/transform_click_event.ts @@ -30,6 +30,9 @@ export interface BrushTriggerEvent { type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>; +// TODO: replace when exported from elastic/charts +const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__'; + /** * returns accessor value from string or function accessor * @param datum @@ -82,6 +85,29 @@ const getAllSplitAccessors = ( value, ]); +/** + * Gets value from small multiple accessors + * + * Only handles single small multiple accessor + */ +function getSplitChartValue({ + smHorizontalAccessorValue, + smVerticalAccessorValue, +}: Pick): + | string + | number + | undefined { + if (smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) { + return smHorizontalAccessorValue; + } + + if (smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) { + return smVerticalAccessorValue; + } + + return; +} + /** * Reduces matching column indexes * @@ -92,7 +118,8 @@ const getAllSplitAccessors = ( const columnReducer = ( xAccessor: Accessor | AccessorFn | null, yAccessor: Accessor | AccessorFn | null, - splitAccessors: AllSeriesAccessors + splitAccessors: AllSeriesAccessors, + splitChartAccessor?: Accessor | AccessorFn ) => ( acc: Array<[index: number, id: string]>, { id }: Datatable['columns'][number], @@ -101,6 +128,7 @@ const columnReducer = ( if ( (xAccessor !== null && validateAccessorId(id, xAccessor)) || (yAccessor !== null && validateAccessorId(id, yAccessor)) || + (splitChartAccessor !== undefined && validateAccessorId(id, splitChartAccessor)) || splitAccessors.some(([accessor]) => validateAccessorId(id, accessor)) ) { acc.push([index, id]); @@ -121,13 +149,18 @@ const rowFindPredicate = ( geometry: GeometryValue | null, xAccessor: Accessor | AccessorFn | null, yAccessor: Accessor | AccessorFn | null, - splitAccessors: AllSeriesAccessors + splitAccessors: AllSeriesAccessors, + splitChartAccessor?: Accessor | AccessorFn, + splitChartValue?: string | number ) => (row: Datatable['rows'][number]): boolean => (geometry === null || (xAccessor !== null && getAccessorValue(row, xAccessor) === geometry.x && yAccessor !== null && - getAccessorValue(row, yAccessor) === geometry.y)) && + getAccessorValue(row, yAccessor) === geometry.y && + (splitChartAccessor === undefined || + (splitChartValue !== undefined && + getAccessorValue(row, splitChartAccessor) === splitChartValue)))) && [...splitAccessors].every(([accessor, value]) => getAccessorValue(row, accessor) === value); /** @@ -142,19 +175,28 @@ export const getFilterFromChartClickEventFn = ( table: Datatable, xAccessor: Accessor | AccessorFn, splitSeriesAccessorFnMap?: Map, + splitChartAccessor?: Accessor | AccessorFn, negate: boolean = false ) => (points: Array<[GeometryValue, XYChartSeriesIdentifier]>): ClickTriggerEvent => { const data: ValueClickContext['data']['data'] = []; points.forEach((point) => { const [geometry, { yAccessor, splitAccessors }] = point; + const splitChartValue = getSplitChartValue(point[1]); const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap); const columns = table.columns.reduce>( - columnReducer(xAccessor, yAccessor, allSplitAccessors), + columnReducer(xAccessor, yAccessor, allSplitAccessors, splitChartAccessor), [] ); const row = table.rows.findIndex( - rowFindPredicate(geometry, xAccessor, yAccessor, allSplitAccessors) + rowFindPredicate( + geometry, + xAccessor, + yAccessor, + allSplitAccessors, + splitChartAccessor, + splitChartValue + ) ); const newData = columns.map(([column, id]) => ({ table, @@ -179,16 +221,20 @@ export const getFilterFromChartClickEventFn = ( * Helper function to get filter action event from series */ export const getFilterFromSeriesFn = (table: Datatable) => ( - { splitAccessors }: XYChartSeriesIdentifier, + { splitAccessors, ...rest }: XYChartSeriesIdentifier, splitSeriesAccessorFnMap?: Map, + splitChartAccessor?: Accessor | AccessorFn, negate = false ): ClickTriggerEvent => { + const splitChartValue = getSplitChartValue(rest); const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap); const columns = table.columns.reduce>( - columnReducer(null, null, allSplitAccessors), + columnReducer(null, null, allSplitAccessors, splitChartAccessor), [] ); - const row = table.rows.findIndex(rowFindPredicate(null, null, null, allSplitAccessors)); + const row = table.rows.findIndex( + rowFindPredicate(null, null, null, allSplitAccessors, splitChartAccessor, splitChartValue) + ); const data: ValueClickContext['data']['data'] = columns.map(([column, id]) => ({ table, column, diff --git a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js index 157523cdf09f4..ee9bed141fe4b 100644 --- a/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js +++ b/src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js @@ -7,7 +7,7 @@ */ import d3 from 'd3'; -import _ from 'lodash'; +import { isNumber, reduce, times } from 'lodash'; import moment from 'moment'; import { InvalidLogScaleValues } from '../../errors'; @@ -62,7 +62,7 @@ export class AxisScale { return d3[extent]( opts.reduce(function (opts, v) { - if (!_.isNumber(v)) v = +v; + if (!isNumber(v)) v = +v; if (!isNaN(v)) opts.push(v); return opts; }, []) @@ -90,7 +90,7 @@ export class AxisScale { const y = moment(x); const method = n > 0 ? 'add' : 'subtract'; - _.times(Math.abs(n), function () { + times(Math.abs(n), function () { y[method](interval); }); @@ -100,7 +100,7 @@ export class AxisScale { getAllPoints() { const config = this.axisConfig; const data = this.visConfig.data.chartData(); - const chartPoints = _.reduce( + const chartPoints = reduce( data, (chartPoints, chart, chartIndex) => { const points = chart.series.reduce((points, seri, seriIndex) => { @@ -254,6 +254,6 @@ export class AxisScale { } validateScale(scale) { - if (!scale || _.isNaN(scale)) throw new Error('scale is ' + scale); + if (!scale || Number.isNaN(scale)) throw new Error('scale is ' + scale); } } diff --git a/src/plugins/vis_type_xy/public/chart_splitter.tsx b/src/plugins/vis_type_xy/public/chart_splitter.tsx new file mode 100644 index 0000000000000..bf63ac1896bd1 --- /dev/null +++ b/src/plugins/vis_type_xy/public/chart_splitter.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { Accessor, AccessorFn, GroupBy, GroupBySort, SmallMultiples } from '@elastic/charts'; + +interface ChartSplitterProps { + splitColumnAccessor?: Accessor | AccessorFn; + splitRowAccessor?: Accessor | AccessorFn; + sort?: GroupBySort; +} + +const CHART_SPLITTER_ID = '__chart_splitter__'; + +export const ChartSplitter = ({ + splitColumnAccessor, + splitRowAccessor, + sort, +}: ChartSplitterProps) => + splitColumnAccessor || splitRowAccessor ? ( + <> + { + const splitTypeAccessor = splitColumnAccessor || splitRowAccessor; + if (splitTypeAccessor) { + return typeof splitTypeAccessor === 'function' + ? splitTypeAccessor(datum) + : datum[splitTypeAccessor]; + } + return spec.id; + }} + sort={sort || 'dataIndex'} + /> + + + ) : null; diff --git a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx index 49b2ab483bc55..02c7157d32c27 100644 --- a/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx +++ b/src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx @@ -16,19 +16,20 @@ import { XYChartSeriesIdentifier, } from '@elastic/charts'; -import { BUCKET_TYPES } from '../../../data/public'; - import { Aspects } from '../types'; import './_detailed_tooltip.scss'; import { fillEmptyValue } from '../utils/get_series_name_fn'; -import { COMPLEX_SPLIT_ACCESSOR } from '../utils/accessors'; +import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors'; interface TooltipData { label: string; value: string; } +// TODO: replace when exported from elastic/charts +const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__'; + const getTooltipData = ( aspects: Aspects, header: TooltipValue | null, @@ -37,10 +38,7 @@ const getTooltipData = ( const data: TooltipData[] = []; if (header) { - const xFormatter = - aspects.x.aggType === BUCKET_TYPES.DATE_RANGE || aspects.x.aggType === BUCKET_TYPES.RANGE - ? null - : aspects.x.formatter; + const xFormatter = isRangeAggType(aspects.x.aggType) ? null : aspects.x.formatter; data.push({ label: aspects.x.title, value: xFormatter ? xFormatter(header.value) : `${header.value}`, @@ -80,6 +78,28 @@ const getTooltipData = ( } }); + if ( + aspects.splitColumn && + valueSeries.smHorizontalAccessorValue !== undefined && + valueSeries.smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE + ) { + data.push({ + label: aspects.splitColumn.title, + value: `${valueSeries.smHorizontalAccessorValue}`, + }); + } + + if ( + aspects.splitRow && + valueSeries.smVerticalAccessorValue !== undefined && + valueSeries.smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE + ) { + data.push({ + label: aspects.splitRow.title, + value: `${valueSeries.smVerticalAccessorValue}`, + }); + } + return data; }; diff --git a/src/plugins/vis_type_xy/public/components/index.ts b/src/plugins/vis_type_xy/public/components/index.ts index 260c08e0fc4a9..9b2559bafd18e 100644 --- a/src/plugins/vis_type_xy/public/components/index.ts +++ b/src/plugins/vis_type_xy/public/components/index.ts @@ -11,4 +11,3 @@ export { XYEndzones } from './xy_endzones'; export { XYCurrentTime } from './xy_current_time'; export { XYSettings } from './xy_settings'; export { XYThresholdLine } from './xy_threshold_line'; -export { SplitChartWarning } from './split_chart_warning'; diff --git a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx b/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx deleted file mode 100644 index b708590e04479..0000000000000 --- a/src/plugins/vis_type_xy/public/components/split_chart_warning.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React, { FC } from 'react'; - -import { EuiLink, EuiCallOut } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { getDocLinks } from '../services'; - -export const SplitChartWarning: FC = () => { - const advancedSettingsLink = getDocLinks().links.management.visualizationSettings; - - return ( - - - - - ), - }} - /> - - ); -}; diff --git a/src/plugins/vis_type_xy/public/config/get_aspects.ts b/src/plugins/vis_type_xy/public/config/get_aspects.ts index b8da4386806d4..c031d3fa1fb9b 100644 --- a/src/plugins/vis_type_xy/public/config/get_aspects.ts +++ b/src/plugins/vis_type_xy/public/config/get_aspects.ts @@ -29,7 +29,10 @@ export function getEmptyAspect(): Aspect { }, }; } -export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dimensions): Aspects { +export function getAspects( + columns: DatatableColumn[], + { x, y, z, series, splitColumn, splitRow }: Dimensions +): Aspects { const seriesDimensions = Array.isArray(series) || series === undefined ? series : [series]; return { @@ -37,6 +40,8 @@ export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dime y: getAspectsFromDimension(columns, y) ?? [], z: z && z?.length > 0 ? getAspectsFromDimension(columns, z[0]) : undefined, series: getAspectsFromDimension(columns, seriesDimensions), + splitColumn: splitColumn?.length ? getAspectsFromDimension(columns, splitColumn[0]) : undefined, + splitRow: splitRow?.length ? getAspectsFromDimension(columns, splitRow[0]) : undefined, }; } diff --git a/src/plugins/vis_type_xy/public/types/config.ts b/src/plugins/vis_type_xy/public/types/config.ts index af3d840739f17..9d4660afa1634 100644 --- a/src/plugins/vis_type_xy/public/types/config.ts +++ b/src/plugins/vis_type_xy/public/types/config.ts @@ -43,6 +43,8 @@ export interface Aspects { y: Aspect[]; z?: Aspect; series?: Aspect[]; + splitColumn?: Aspect; + splitRow?: Aspect; } export interface AxisGrid { diff --git a/src/plugins/vis_type_xy/public/utils/accessors.tsx b/src/plugins/vis_type_xy/public/utils/accessors.tsx index d1337251d36aa..e40248ae92e12 100644 --- a/src/plugins/vis_type_xy/public/utils/accessors.tsx +++ b/src/plugins/vis_type_xy/public/utils/accessors.tsx @@ -26,11 +26,15 @@ const getFieldName = (fieldName: string, index?: number) => { return `${fieldName}${indexStr}`; }; +export const isRangeAggType = (type: string | null) => + type === BUCKET_TYPES.DATE_RANGE || type === BUCKET_TYPES.RANGE; + /** * Returns accessor function for complex accessor types * @param aspect + * @param isComplex - forces to be functional/complex accessor */ -export const getComplexAccessor = (fieldName: string) => ( +export const getComplexAccessor = (fieldName: string, isComplex: boolean = false) => ( aspect: Aspect, index?: number ): Accessor | AccessorFn | undefined => { @@ -38,12 +42,7 @@ export const getComplexAccessor = (fieldName: string) => ( return; } - if ( - !( - (aspect.aggType === BUCKET_TYPES.DATE_RANGE || aspect.aggType === BUCKET_TYPES.RANGE) && - aspect.formatter - ) - ) { + if (!((isComplex || isRangeAggType(aspect.aggType)) && aspect.formatter)) { return aspect.accessor; } @@ -51,7 +50,7 @@ export const getComplexAccessor = (fieldName: string) => ( const accessor = aspect.accessor; const fn: AccessorFn = (d) => { const v = d[accessor]; - if (!v) { + if (v === undefined) { return; } const f = formatter(v); diff --git a/src/plugins/vis_type_xy/public/vis_component.tsx b/src/plugins/vis_type_xy/public/vis_component.tsx index 6f994707cbb72..871fb408d4da0 100644 --- a/src/plugins/vis_type_xy/public/vis_component.tsx +++ b/src/plugins/vis_type_xy/public/vis_component.tsx @@ -65,6 +65,7 @@ import { getComplexAccessor, getSplitSeriesAccessorFnMap, } from './utils/accessors'; +import { ChartSplitter } from './chart_splitter'; export interface VisComponentProps { visParams: VisParams; @@ -117,7 +118,8 @@ const VisComponent = (props: VisComponentProps) => { ( visData: Datatable, xAccessor: Accessor | AccessorFn, - splitSeriesAccessors: Array + splitSeriesAccessors: Array, + splitChartAccessor?: Accessor | AccessorFn ): ElementClickListener => { const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors); return (elements) => { @@ -125,7 +127,8 @@ const VisComponent = (props: VisComponentProps) => { const event = getFilterFromChartClickEventFn( visData, xAccessor, - splitSeriesAccessorFnMap + splitSeriesAccessorFnMap, + splitChartAccessor )(elements as XYChartElementEvent[]); props.fireEvent(event); } @@ -154,12 +157,17 @@ const VisComponent = (props: VisComponentProps) => { ( visData: Datatable, xAccessor: Accessor | AccessorFn, - splitSeriesAccessors: Array + splitSeriesAccessors: Array, + splitChartAccessor?: Accessor | AccessorFn ) => { const splitSeriesAccessorFnMap = getSplitSeriesAccessorFnMap(splitSeriesAccessors); return (series: XYChartSeriesIdentifier): ClickTriggerEvent | null => { if (xAccessor !== null) { - return getFilterFromSeriesFn(visData)(series, splitSeriesAccessorFnMap); + return getFilterFromSeriesFn(visData)( + series, + splitSeriesAccessorFnMap, + splitChartAccessor + ); } return null; @@ -304,6 +312,12 @@ const VisComponent = (props: VisComponentProps) => { : [], [config.aspects.series] ); + const splitChartColumnAccessor = config.aspects.splitColumn + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitColumn) + : undefined; + const splitChartRowAccessor = config.aspects.splitRow + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitRow) + : undefined; const renderSeries = useMemo( () => @@ -336,6 +350,10 @@ const VisComponent = (props: VisComponentProps) => { legendPosition={legendPosition} /> + { xDomain={xDomain} adjustedXDomain={adjustedXDomain} legendColorPicker={useColorPicker(legendPosition, setColor, getSeriesName)} - onElementClick={handleFilterClick(visData, xAccessor, splitSeriesAccessors)} + onElementClick={handleFilterClick( + visData, + xAccessor, + splitSeriesAccessors, + splitChartColumnAccessor ?? splitChartRowAccessor + )} onBrushEnd={handleBrush(visData, xAccessor, 'interval' in config.aspects.x.params)} onRenderChange={onRenderChange} legendAction={ config.aspects.series && (config.aspects.series?.length ?? 0) > 0 ? getLegendActions( canFilter, - getFilterEventData(visData, xAccessor, splitSeriesAccessors), + getFilterEventData( + visData, + xAccessor, + splitSeriesAccessors, + splitChartColumnAccessor ?? splitChartRowAccessor + ), handleFilterAction, getSeriesName ) diff --git a/src/plugins/vis_type_xy/public/vis_renderer.tsx b/src/plugins/vis_type_xy/public/vis_renderer.tsx index 612388939d26b..1a47742b3d004 100644 --- a/src/plugins/vis_type_xy/public/vis_renderer.tsx +++ b/src/plugins/vis_type_xy/public/vis_renderer.tsx @@ -16,7 +16,6 @@ import { VisualizationContainer } from '../../visualizations/public'; import type { PersistedState } from '../../visualizations/public'; import { XyVisType } from '../common'; -import { SplitChartWarning } from './components/split_chart_warning'; import { VisComponentType } from './vis_component'; import { RenderValue, visName } from './xy_vis_fn'; @@ -36,24 +35,20 @@ export const xyVisRenderer: ExpressionRenderDefinition = { reuseDomNode: true, render: async (domNode, { visData, visConfig, visType, syncColors }, handlers) => { const showNoResult = shouldShowNoResultsMessage(visData, visType); - const isSplitChart = Boolean(visConfig.dimensions.splitRow); handlers.onDestroy(() => unmountComponentAtNode(domNode)); render( - <> - {isSplitChart && } - - - - + + + , domNode ); diff --git a/src/plugins/vis_type_xy/public/vis_types/area.tsx b/src/plugins/vis_type_xy/public/vis_types/area.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/area.tsx rename to src/plugins/vis_type_xy/public/vis_types/area.ts index 50721c349d6e9..09007a01ca8bc 100644 --- a/src/plugins/vis_type_xy/public/vis_types/area.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/area.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; -import { SplitTooltip } from './split_tooltip'; export const getAreaVisTypeDefinition = ( showElasticChartsOptions = false @@ -181,12 +178,6 @@ export const getAreaVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx b/src/plugins/vis_type_xy/public/vis_types/histogram.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/histogram.tsx rename to src/plugins/vis_type_xy/public/vis_types/histogram.ts index 4fc8dbbb80e7b..daae5f5e48e61 100644 --- a/src/plugins/vis_type_xy/public/vis_types/histogram.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/histogram.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; import { defaultCountLabel, LabelRotation } from '../../../charts/public'; -import { SplitTooltip } from './split_tooltip'; export const getHistogramVisTypeDefinition = ( showElasticChartsOptions = false @@ -184,12 +181,6 @@ export const getHistogramVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx rename to src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts index b53bb7bc9dd40..9e026fa0d7474 100644 --- a/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/horizontal_bar.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; import { defaultCountLabel, LabelRotation } from '../../../charts/public'; -import { SplitTooltip } from './split_tooltip'; export const getHorizontalBarVisTypeDefinition = ( showElasticChartsOptions = false @@ -183,12 +180,6 @@ export const getHorizontalBarVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/line.tsx b/src/plugins/vis_type_xy/public/vis_types/line.ts similarity index 94% rename from src/plugins/vis_type_xy/public/vis_types/line.tsx rename to src/plugins/vis_type_xy/public/vis_types/line.ts index e9b0533b957f5..3f3087207fa19 100644 --- a/src/plugins/vis_type_xy/public/vis_types/line.tsx +++ b/src/plugins/vis_type_xy/public/vis_types/line.ts @@ -6,8 +6,6 @@ * Public License, v 1. */ -import React from 'react'; - import { i18n } from '@kbn/i18n'; // @ts-ignore import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; @@ -30,7 +28,6 @@ import { toExpressionAst } from '../to_ast'; import { ChartType } from '../../common'; import { getConfigCollections } from '../editor/collections'; import { getOptionTabs } from '../editor/common_config'; -import { SplitTooltip } from './split_tooltip'; export const getLineVisTypeDefinition = ( showElasticChartsOptions = false @@ -175,12 +172,6 @@ export const getLineVisTypeDefinition = ( min: 0, max: 1, aggFilter: ['!geohash_grid', '!geotile_grid', '!filter'], - // TODO: Remove when split chart aggs are supported - // https://github.com/elastic/kibana/issues/82496 - ...(showElasticChartsOptions && { - disabled: true, - tooltip: , - }), }, ], }, diff --git a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx b/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx deleted file mode 100644 index ca22136599341..0000000000000 --- a/src/plugins/vis_type_xy/public/vis_types/split_tooltip.tsx +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; - -import { FormattedMessage } from '@kbn/i18n/react'; - -export function SplitTooltip() { - return ( - - ); -} diff --git a/src/plugins/vis_type_xy/server/plugin.ts b/src/plugins/vis_type_xy/server/plugin.ts index bd7957164fd1a..fa3dddfeca02a 100644 --- a/src/plugins/vis_type_xy/server/plugin.ts +++ b/src/plugins/vis_type_xy/server/plugin.ts @@ -24,8 +24,7 @@ export const uiSettingsConfig: Record> = { description: i18n.translate( 'visTypeXy.advancedSettings.visualization.legacyChartsLibrary.description', { - defaultMessage: - 'Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.', + defaultMessage: 'Enables legacy charts library for area, line and bar charts in visualize.', } ), category: ['visualization'], diff --git a/test/functional/apps/visualize/_line_chart_split_chart.ts b/test/functional/apps/visualize/_line_chart_split_chart.ts index aeb80a58c9655..3e74bf0b7c0ec 100644 --- a/test/functional/apps/visualize/_line_chart_split_chart.ts +++ b/test/functional/apps/visualize/_line_chart_split_chart.ts @@ -176,8 +176,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers(); - const minLabel = 2; - const maxLabel = 5000; + const minLabel = await PageObjects.visChart.getExpectedValue(2, 1); + const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000); const numberOfLabels = 10; expect(labels.length).to.be.greaterThan(numberOfLabels); expect(labels[0]).to.eql(minLabel); @@ -188,8 +188,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabelsAsNumbers(); - const minLabel = 2; - const maxLabel = 5000; + const minLabel = await PageObjects.visChart.getExpectedValue(2, 1); + const maxLabel = await PageObjects.visChart.getExpectedValue(5000, 7000); const numberOfLabels = 10; expect(labels.length).to.be.greaterThan(numberOfLabels); expect(labels[0]).to.eql(minLabel); @@ -201,7 +201,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, false); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['0', '2,000', '4,000', '6,000', '8,000', '10,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -210,7 +213,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['2,000', '4,000', '6,000', '8,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -220,7 +226,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); log.debug(labels); - const expectedLabels = ['0', '2,000', '4,000', '6,000', '8,000', '10,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['0', '2,000', '4,000', '6,000', '8,000', '10,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); @@ -228,7 +237,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visEditor.changeYAxisFilterLabelsCheckbox(axisId, true); await PageObjects.visEditor.clickGo(); const labels = await PageObjects.visChart.getYAxisLabels(); - const expectedLabels = ['2,000', '4,000', '6,000', '8,000']; + const expectedLabels = await PageObjects.visChart.getExpectedValue( + ['2,000', '4,000', '6,000', '8,000'], + ['0', '1,000', '2,000', '3,000', '4,000', '5,000', '6,000', '7,000', '8,000', '9,000'] + ); expect(labels).to.eql(expectedLabels); }); }); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index dddcd82f1d3f8..8dd2854419693 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -52,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { // Test replaced vislib chart types loadTestFile(require.resolve('./_area_chart')); loadTestFile(require.resolve('./_line_chart_split_series')); + loadTestFile(require.resolve('./_line_chart_split_chart')); loadTestFile(require.resolve('./_point_series_options')); loadTestFile(require.resolve('./_vertical_bar_chart')); loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex')); From 16657028f1b188d959a1384a82e4557f0a168e09 Mon Sep 17 00:00:00 2001 From: Daniil Date: Mon, 25 Jan 2021 16:16:25 +0300 Subject: [PATCH 03/90] [Saved Objects] Fix saved object view path (#89057) * Fix saved object view path * Add additional check --- .../public/lib/create_field_list.ts | 3 ++- .../object_view/components/form.tsx | 5 ++-- .../object_view/saved_object_view.tsx | 18 +++++++------ .../saved_objects_edition_page.tsx | 26 +++++++++++-------- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/plugins/saved_objects_management/public/lib/create_field_list.ts b/src/plugins/saved_objects_management/public/lib/create_field_list.ts index cd30a02bd0ef3..4497fb04ffa2c 100644 --- a/src/plugins/saved_objects_management/public/lib/create_field_list.ts +++ b/src/plugins/saved_objects_management/public/lib/create_field_list.ts @@ -11,11 +11,12 @@ import { SimpleSavedObject } from '../../../../core/public'; import { castEsToKbnFieldTypeName } from '../../../data/public'; import { ObjectField } from '../management_section/types'; import { SavedObjectLoader } from '../../../saved_objects/public'; +import { SavedObjectWithMetadata } from '../types'; const maxRecursiveIterations = 20; export function createFieldList( - object: SimpleSavedObject, + object: SimpleSavedObject | SavedObjectWithMetadata, service?: SavedObjectLoader ): ObjectField[] { let fields = Object.entries(object.attributes as Record).reduce( diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx index 96a4a24f6591e..e048b92b9566c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/form.tsx @@ -19,14 +19,15 @@ import { set } from '@elastic/safer-lodash-set'; import { cloneDeep } from 'lodash'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SimpleSavedObject, SavedObjectsClientContract } from '../../../../../../core/public'; +import { SavedObjectsClientContract } from '../../../../../../core/public'; import { SavedObjectLoader } from '../../../../../saved_objects/public'; import { Field } from './field'; import { ObjectField, FieldState, SubmittedFormData } from '../../types'; import { createFieldList } from '../../../lib'; +import { SavedObjectWithMetadata } from '../../../types'; interface FormProps { - object: SimpleSavedObject; + object: SavedObjectWithMetadata; service: SavedObjectLoader; savedObjectsClient: SavedObjectsClientContract; editionEnabled: boolean; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx index 31c0a76e16f58..3343e0a63f54c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.tsx @@ -14,16 +14,18 @@ import { SavedObjectsClientContract, OverlayStart, NotificationsStart, - SimpleSavedObject, ScopedHistory, + HttpSetup, } from '../../../../../core/public'; import { ISavedObjectsManagementServiceRegistry } from '../../services'; import { Header, NotFoundErrors, Intro, Form } from './components'; -import { canViewInApp } from '../../lib'; +import { canViewInApp, findObject } from '../../lib'; import { SubmittedFormData } from '../types'; +import { SavedObjectWithMetadata } from '../../types'; interface SavedObjectEditionProps { id: string; + http: HttpSetup; serviceName: string; serviceRegistry: ISavedObjectsManagementServiceRegistry; capabilities: Capabilities; @@ -36,7 +38,7 @@ interface SavedObjectEditionProps { interface SavedObjectEditionState { type: string; - object?: SimpleSavedObject; + object?: SavedObjectWithMetadata; } export class SavedObjectEdition extends Component< @@ -56,9 +58,9 @@ export class SavedObjectEdition extends Component< } componentDidMount() { - const { id, savedObjectsClient } = this.props; + const { http, id } = this.props; const { type } = this.state; - savedObjectsClient.get(type, id).then((object) => { + findObject(http, type, id).then((object) => { this.setState({ object, }); @@ -70,7 +72,7 @@ export class SavedObjectEdition extends Component< capabilities, notFoundType, serviceRegistry, - id, + http, serviceName, savedObjectsClient, } = this.props; @@ -80,7 +82,7 @@ export class SavedObjectEdition extends Component< string, boolean >; - const canView = canViewInApp(capabilities, type); + const canView = canViewInApp(capabilities, type) && Boolean(object?.meta.inAppUrl?.path); const service = serviceRegistry.get(serviceName)!.service; return ( @@ -91,7 +93,7 @@ export class SavedObjectEdition extends Component< canViewInApp={canView} type={type} onDeleteClick={() => this.delete()} - viewUrl={service.urlFor(id)} + viewUrl={http.basePath.prepend(object?.meta.inAppUrl?.path || '')} /> {notFoundType && ( <> diff --git a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx index 758789aa0f47e..2af7c22488c51 100644 --- a/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx +++ b/src/plugins/saved_objects_management/public/management_section/saved_objects_edition_page.tsx @@ -11,6 +11,7 @@ import { useParams, useLocation } from 'react-router-dom'; import { parse } from 'query-string'; import { i18n } from '@kbn/i18n'; import { CoreStart, ChromeBreadcrumb, ScopedHistory } from 'src/core/public'; +import { RedirectAppLinks } from '../../../kibana_react/public'; import { ISavedObjectsManagementServiceRegistry } from '../services'; import { SavedObjectEdition } from './object_view'; @@ -50,17 +51,20 @@ const SavedObjectsEditionPage = ({ }, [setBreadcrumbs, service]); return ( - + + + ); }; From 50d8c69ea800f778c8530a1c56036ea590c7569d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 13:36:38 +0000 Subject: [PATCH 04/90] skip flaky suite (#88639) --- .../functional/tests/visualize_integration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts index e92ba226f3959..51f4bf8883521 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts @@ -151,7 +151,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('editing', () => { + // FLAKY: https://github.com/elastic/kibana/issues/88639 + describe.skip('editing', () => { beforeEach(async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await listingTable.waitUntilTableIsLoaded(); From 99ffbbe357b5f80f10925f71e7d5862403f0a009 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Mon, 25 Jan 2021 14:46:23 +0100 Subject: [PATCH 05/90] [Lens] Restore a11y flacky tests (#88954) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/accessibility/apps/lens.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/test/accessibility/apps/lens.ts b/x-pack/test/accessibility/apps/lens.ts index a7cacd0ad1cbb..71673d49c0c08 100644 --- a/x-pack/test/accessibility/apps/lens.ts +++ b/x-pack/test/accessibility/apps/lens.ts @@ -7,15 +7,12 @@ import { FtrProviderContext } from '../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'visualize', 'header', 'home', 'settings', 'lens']); + const PageObjects = getPageObjects(['common', 'visualize', 'timePicker', 'home', 'lens']); const a11y = getService('a11y'); const testSubjects = getService('testSubjects'); const listingTable = getService('listingTable'); - // FLAKY: https://github.com/elastic/kibana/issues/88926 - // FLAKY: https://github.com/elastic/kibana/issues/88927 - // FLAKY: https://github.com/elastic/kibana/issues/88929 - describe.skip('Lens', () => { + describe('Lens', () => { const lensChartName = 'MyLensChart'; before(async () => { await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { @@ -35,12 +32,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('lens', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await a11y.testAppSnapshot(); }); it('lens XY chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await PageObjects.lens.configureDimension({ dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension', @@ -75,6 +74,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('dimension configuration panel', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await PageObjects.timePicker.ensureHiddenNoDataPopover(); await PageObjects.lens.openDimensionEditor('lnsXY_xDimensionPanel > lns-empty-dimension'); await a11y.testAppSnapshot(); From a5bb86482d346c905d2946e667055cc0889d320a Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 25 Jan 2021 13:49:20 +0000 Subject: [PATCH 06/90] skip flaky suite (#89069) --- .../apps/management/search_sessions/sessions_management.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts index f06e8eba0bf68..e3797550984aa 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/management/search_sessions/sessions_management.ts @@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const retry = getService('retry'); - describe('Search search sessions Management UI', () => { + // FLAKY: https://github.com/elastic/kibana/issues/89069 + describe.skip('Search search sessions Management UI', () => { describe('New search sessions', () => { before(async () => { await PageObjects.common.navigateToApp('dashboard'); From aeb6df30d5b987579fcef03805a17e3c5b7da224 Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Mon, 25 Jan 2021 13:51:57 +0000 Subject: [PATCH 07/90] Update user management page (#87133) * Update user management page * Fixed i18n errors * Fix linting errors * Add ids required for accessability * Added suggestions from code review * Fix test errors * Fix types in fleet * fix translations * Fix i18n * Added suggestions from code review * Fix i18n errors * Fix linting errors * Update messaging * Updated unit tests * Updated functional tests * Fixed functional tests * Fix linting errors * Fix React warnings * Added suggestions from code review * Added tests and renamed routes * Fix functional tests * Simplified API integration tests * Updated copy based on writing suggestions * Fixed unit tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../components/agent_logs/agent_logs.tsx | 2 +- x-pack/plugins/security/common/constants.ts | 13 + x-pack/plugins/security/common/model/index.ts | 2 + x-pack/plugins/security/common/model/role.ts | 22 +- .../security/public/components/breadcrumb.tsx | 137 +++ .../public/components/confirm_modal.tsx | 94 ++ .../security/public/components/doc_link.tsx | 60 ++ .../public/components/form_flyout.tsx | 97 ++ .../public/components/use_current_user.ts | 24 + .../security/public/components/use_form.ts | 205 +++++ .../security/public/components/use_html_id.ts | 27 + .../role_combo_box/role_combo_box.test.tsx | 266 +++--- .../role_combo_box/role_combo_box.tsx | 128 ++- .../role_combo_box_option.test.tsx | 57 -- .../role_combo_box/role_combo_box_option.tsx | 31 - .../roles/edit_role/edit_role_page.tsx | 2 +- .../roles/edit_role/validate_role.test.ts | 28 +- .../roles/edit_role/validate_role.ts | 22 +- .../edit_user/change_password_flyout.tsx | 286 ++++++ .../users/edit_user/confirm_delete_users.tsx | 96 ++ .../users/edit_user/confirm_disable_users.tsx | 119 +++ .../users/edit_user/confirm_enable_users.tsx | 89 ++ .../users/edit_user/create_user_page.test.tsx | 108 +++ .../users/edit_user/create_user_page.tsx | 44 + .../users/edit_user/edit_user_page.scss | 6 - .../users/edit_user/edit_user_page.test.tsx | 566 ++++++++---- .../users/edit_user/edit_user_page.tsx | 836 ++++++------------ .../management/users/edit_user/index.ts | 1 + .../management/users/edit_user/user_form.tsx | 466 ++++++++++ .../users/edit_user/validate_user.test.ts | 128 --- .../users/edit_user/validate_user.ts | 142 --- .../management/users/user_api_client.mock.ts | 2 + .../management/users/user_api_client.ts | 10 +- .../users/users_grid/users_grid_page.tsx | 2 +- .../users/users_management_app.test.tsx | 129 +-- .../management/users/users_management_app.tsx | 158 ++-- .../server/routes/users/create_or_update.ts | 7 +- .../security/server/routes/users/disable.ts | 32 + .../security/server/routes/users/enable.ts | 32 + .../security/server/routes/users/index.ts | 4 + .../translations/translations/ja-JP.json | 34 +- .../translations/translations/zh-CN.json | 34 +- x-pack/test/accessibility/apps/users.ts | 47 +- .../api_integration/apis/security/index.ts | 1 + .../apis/security/security_basic.ts | 1 + .../api_integration/apis/security/users.ts | 47 + .../dashboard_mode/dashboard_view_mode.js | 58 +- .../apps/security/doc_level_security_roles.js | 8 +- .../apps/security/field_level_security.js | 16 +- .../functional/apps/security/rbac_phase1.js | 14 +- .../functional/apps/security/role_mappings.ts | 5 +- .../apps/security/secure_roles_perm.js | 8 +- .../functional/apps/security/user_email.js | 7 +- x-pack/test/functional/apps/security/users.js | 12 +- .../functional/page_objects/security_page.ts | 108 ++- yarn.lock | 40 +- 57 files changed, 3242 insertions(+), 1680 deletions(-) create mode 100644 x-pack/plugins/security/public/components/breadcrumb.tsx create mode 100644 x-pack/plugins/security/public/components/confirm_modal.tsx create mode 100644 x-pack/plugins/security/public/components/doc_link.tsx create mode 100644 x-pack/plugins/security/public/components/form_flyout.tsx create mode 100644 x-pack/plugins/security/public/components/use_current_user.ts create mode 100644 x-pack/plugins/security/public/components/use_form.ts create mode 100644 x-pack/plugins/security/public/components/use_html_id.ts delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.test.tsx delete mode 100644 x-pack/plugins/security/public/management/role_combo_box/role_combo_box_option.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/change_password_flyout.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_delete_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_disable_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/confirm_enable_users.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx create mode 100644 x-pack/plugins/security/public/management/users/edit_user/create_user_page.tsx delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/edit_user_page.scss create mode 100644 x-pack/plugins/security/public/management/users/edit_user/user_form.tsx delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.test.ts delete mode 100644 x-pack/plugins/security/public/management/users/edit_user/validate_user.ts create mode 100644 x-pack/plugins/security/server/routes/users/disable.ts create mode 100644 x-pack/plugins/security/server/routes/users/enable.ts create mode 100644 x-pack/test/api_integration/apis/security/users.ts diff --git a/package.json b/package.json index 2fdc31820b9d4..24297011ccc63 100644 --- a/package.json +++ b/package.json @@ -287,7 +287,7 @@ "react-resizable": "^1.7.5", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", - "react-use": "^13.27.0", + "react-use": "^15.3.4", "recompose": "^0.26.0", "redux": "^4.0.5", "redux-actions": "^2.6.5", diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx index 7326d2efb8565..8a4cf1d8566a0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/agent_logs.tsx @@ -183,7 +183,7 @@ export const AgentLogsUI: React.FunctionComponent = memo(({ agen [http.basePath, state.start, state.end, logStreamQuery] ); - const [logsPanelRef, { height: logPanelHeight }] = useMeasure(); + const [logsPanelRef, { height: logPanelHeight }] = useMeasure(); const agentVersion = agent.local_metadata?.elastic?.agent?.version; const isLogFeatureAvailable = useMemo(() => { diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index f53b5ca6d56ca..c235c296bcbae 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -22,3 +22,16 @@ export const AUTH_PROVIDER_HINT_QUERY_STRING_PARAMETER = 'auth_provider_hint'; export const LOGOUT_PROVIDER_QUERY_STRING_PARAMETER = 'provider'; export const LOGOUT_REASON_QUERY_STRING_PARAMETER = 'msg'; export const NEXT_URL_QUERY_STRING_PARAMETER = 'next'; + +/** + * Matches valid usernames and role names. + * + * - Must contain only letters, numbers, spaces, punctuation and printable symbols. + * - Must not contain leading or trailing spaces. + */ +export const NAME_REGEX = /^(?! )[a-zA-Z0-9 !"#$%&'()*+,\-./\\:;<=>?@\[\]^_`{|}~]+(?) { * @param {role} the Role as returned by roles API */ export function isRoleDeprecated(role: Partial) { - return role.metadata?._deprecated ?? false; + return (role.metadata?._deprecated as boolean) ?? false; +} + +/** + * Returns whether given role is a system role or not. + * + * @param {role} the Role as returned by roles API + */ +export function isRoleSystem(role: Partial) { + return (isRoleReserved(role) && role.name?.endsWith('_system')) ?? false; +} + +/** + * Returns whether given role is an admin role or not. + * + * @param {role} the Role as returned by roles API + */ +export function isRoleAdmin(role: Partial) { + return ( + (isRoleReserved(role) && (role.name?.endsWith('_admin') || role.name === 'superuser')) ?? false + ); } /** diff --git a/x-pack/plugins/security/public/components/breadcrumb.tsx b/x-pack/plugins/security/public/components/breadcrumb.tsx new file mode 100644 index 0000000000000..7246e37b33da9 --- /dev/null +++ b/x-pack/plugins/security/public/components/breadcrumb.tsx @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { createContext, useEffect, useRef, useContext, FunctionComponent } from 'react'; +import { EuiBreadcrumb } from '@elastic/eui'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; + +interface BreadcrumbsContext { + parents: BreadcrumbProps[]; + onMount(breadcrumbs: BreadcrumbProps[]): void; + onUnmount(breadcrumbs: BreadcrumbProps[]): void; +} + +const BreadcrumbsContext = createContext(undefined); + +export interface BreadcrumbProps extends EuiBreadcrumb { + text: string; +} + +/** + * Component that automatically sets breadcrumbs and document title based on the render tree. + * + * @example + * // Breadcrumbs will be set to: "Users > Create" + * // Document title will be set to: "Create - Users" + * + * ```typescript + * + * + * {showForm && ( + * + *
+ *
+ * )} + * + * ``` + */ +export const Breadcrumb: FunctionComponent = ({ children, ...breadcrumb }) => { + const context = useContext(BreadcrumbsContext); + const component = {children}; + + if (context) { + return component; + } + + return {component}; +}; + +export interface BreadcrumbsProviderProps { + onChange?: BreadcrumbsChangeHandler; +} + +export type BreadcrumbsChangeHandler = (breadcrumbs: BreadcrumbProps[]) => void; + +/** + * Component that can be used to define any side effects that should occur when breadcrumbs change. + * + * By default the breadcrumbs in application chrome are set and the document title is updated. + * + * @example + * ```typescript + * setBreadcrumbs(breadcrumbs)}> + * + * + * ``` + */ +export const BreadcrumbsProvider: FunctionComponent = ({ + children, + onChange, +}) => { + const { services } = useKibana(); + const breadcrumbsRef = useRef([]); + + const handleChange = (breadcrumbs: BreadcrumbProps[]) => { + if (onChange) { + onChange(breadcrumbs); + } else if (services.chrome) { + services.chrome.setBreadcrumbs(breadcrumbs); + services.chrome.docTitle.change(getDocTitle(breadcrumbs)); + } + }; + + return ( + { + if (breadcrumbs.length > breadcrumbsRef.current.length) { + breadcrumbsRef.current = breadcrumbs; + handleChange(breadcrumbs); + } + }, + onUnmount: (breadcrumbs) => { + if (breadcrumbs.length < breadcrumbsRef.current.length) { + breadcrumbsRef.current = breadcrumbs; + handleChange(breadcrumbs); + } + }, + }} + > + {children} + + ); +}; + +export interface InnerBreadcrumbProps { + breadcrumb: BreadcrumbProps; +} + +export const InnerBreadcrumb: FunctionComponent = ({ + breadcrumb, + children, +}) => { + const { parents, onMount, onUnmount } = useContext(BreadcrumbsContext)!; + const nextParents = [...parents, breadcrumb]; + + useEffect(() => { + onMount(nextParents); + return () => onUnmount(parents); + }, [breadcrumb.text, breadcrumb.href]); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + + {children} + + ); +}; + +export function getDocTitle(breadcrumbs: BreadcrumbProps[], maxBreadcrumbs = 2) { + return breadcrumbs + .slice(0, maxBreadcrumbs) + .reverse() + .map(({ text }) => text); +} diff --git a/x-pack/plugins/security/public/components/confirm_modal.tsx b/x-pack/plugins/security/public/components/confirm_modal.tsx new file mode 100644 index 0000000000000..8dfbf9e3f0649 --- /dev/null +++ b/x-pack/plugins/security/public/components/confirm_modal.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { + EuiButton, + EuiButtonProps, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiModalProps, + EuiOverlayMask, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface ConfirmModalProps extends Omit { + confirmButtonText: string; + confirmButtonColor?: EuiButtonProps['color']; + isLoading?: EuiButtonProps['isLoading']; + isDisabled?: EuiButtonProps['isDisabled']; + onCancel(): void; + onConfirm(): void; + ownFocus?: boolean; +} + +/** + * Component that renders a confirmation modal similar to `EuiConfirmModal`, except that + * it adds `isLoading` prop, which renders a loading spinner and disables action buttons, + * and `ownFocus` prop to render overlay mask. + */ +export const ConfirmModal: FunctionComponent = ({ + children, + confirmButtonColor: buttonColor, + confirmButtonText, + isLoading, + isDisabled, + onCancel, + onConfirm, + ownFocus = true, + title, + ...rest +}) => { + const modal = ( + + + {title} + + {children} + + + + + + + + + + {confirmButtonText} + + + + + + ); + + return ownFocus ? ( + {modal} + ) : ( + modal + ); +}; diff --git a/x-pack/plugins/security/public/components/doc_link.tsx b/x-pack/plugins/security/public/components/doc_link.tsx new file mode 100644 index 0000000000000..50a93b8ee5090 --- /dev/null +++ b/x-pack/plugins/security/public/components/doc_link.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, FunctionComponent } from 'react'; +import { EuiLink } from '@elastic/eui'; +import { useKibana } from '../../../../../src/plugins/kibana_react/public'; +import { CoreStart } from '../../../../../src/core/public'; + +export type DocLinks = CoreStart['docLinks']['links']; +export type GetDocLinkFunction = (app: string, doc: string) => string; + +/** + * Creates links to the documentation. + * + * @see {@link DocLink} for a component that creates a link to the docs. + * + * @example + * ```typescript + * + * Learn what privileges individual roles grant. + * + * ``` + * + * @example + * ```typescript + * const [docs] = useDocLinks(); + * + * + * Learn how to get started with dashboards. + * + * ``` + */ +export function useDocLinks(): [DocLinks, GetDocLinkFunction] { + const { services } = useKibana(); + const { links, ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = services.docLinks!; + const getDocLink = useCallback( + (app, doc) => { + return `${ELASTIC_WEBSITE_URL}guide/en/${app}/reference/${DOC_LINK_VERSION}/${doc}`; + }, + [ELASTIC_WEBSITE_URL, DOC_LINK_VERSION] + ); + return [links, getDocLink]; +} + +export interface DocLinkProps { + app: string; + doc: string; +} + +export const DocLink: FunctionComponent = ({ app, doc, children }) => { + const [, getDocLink] = useDocLinks(); + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/security/public/components/form_flyout.tsx b/x-pack/plugins/security/public/components/form_flyout.tsx new file mode 100644 index 0000000000000..a0d397f81751e --- /dev/null +++ b/x-pack/plugins/security/public/components/form_flyout.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, FunctionComponent, RefObject } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiTitle, + EuiFlyout, + EuiFlyoutProps, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButton, + EuiButtonProps, + EuiButtonEmpty, + EuiPortal, +} from '@elastic/eui'; +import { useHtmlId } from './use_html_id'; + +export interface FormFlyoutProps extends Omit { + title: string; + initialFocus?: RefObject; + onCancel(): void; + onSubmit(): void; + submitButtonText: string; + submitButtonColor?: EuiButtonProps['color']; + isLoading?: EuiButtonProps['isLoading']; + isDisabled?: EuiButtonProps['isDisabled']; +} + +export const FormFlyout: FunctionComponent = ({ + title, + submitButtonText, + submitButtonColor, + onCancel, + onSubmit, + isLoading, + isDisabled, + children, + initialFocus, + ...rest +}) => { + useEffect(() => { + if (initialFocus && initialFocus.current) { + initialFocus.current.focus(); + } + }, [initialFocus]); + + const titleId = useHtmlId('formFlyout', 'title'); + + return ( + + + + +

{title}

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

Change password

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

+ +

+

+ +

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

+ +

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

+ +

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

+ +

+

+ +

+
+ ) : ( + +

+ +

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

+ +

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

+ +

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

{displayName}

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

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

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

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

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

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

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

+ +

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

Significant terms

+

Correlations

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

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

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

TODO: Analytics overview

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

TODO: Query detail page

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

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

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

-
-
- -
- Request error -
-
-
-`; diff --git a/src/plugins/visualizations/public/components/_visualization.scss b/src/plugins/visualizations/public/components/_visualization.scss index e8d4c5c6db445..42b59b8de93cd 100644 --- a/src/plugins/visualizations/public/components/_visualization.scss +++ b/src/plugins/visualizations/public/components/_visualization.scss @@ -15,48 +15,6 @@ position: relative; padding: $euiSizeS; flex: 1 1 100%; - - /** - * 1. Expand to fill the container but accept being squeezed smaller by the spy, even so small - * that it disappears entirely. - */ - .visChart__container { - @include euiScrollBar; - min-height: 0; - flex: 1 1 0; /* 1 */ - display: flex; - flex-direction: row; - overflow: auto; - transition: opacity .01s; - - // IE11 Hack - // - // Normally we would just set flex: 1 1 0%, which works as expected in IE11. - // Unfortunately, a recent bug in Firefox causes this rule to be ignored, so we - // have to use an IE-specific hack instead. - @include internetExplorerOnly() { - flex: 1 0; - } - - &:focus { - box-shadow: none; - } - } - - // SASSTODO: Can't find exact usage - .loading { - opacity: .5; - } - - // SASSTODO: Can't find exact usage - .spinner { - position: absolute; - top: 40%; - left: 0; - right: 0; - z-index: 20; - opacity: .5; - } } .visChart { diff --git a/src/plugins/visualizations/public/components/index.ts b/src/plugins/visualizations/public/components/index.ts index 88f3acf20d39b..1d6b6b5d911ea 100644 --- a/src/plugins/visualizations/public/components/index.ts +++ b/src/plugins/visualizations/public/components/index.ts @@ -6,6 +6,4 @@ * Public License, v 1. */ -export { Visualization } from './visualization'; export { VisualizationContainer } from './visualization_container'; -export { VisualizationNoResults } from './visualization_noresults'; diff --git a/src/plugins/visualizations/public/components/visualization.test.js b/src/plugins/visualizations/public/components/visualization.test.js deleted file mode 100644 index cb332232cbc55..0000000000000 --- a/src/plugins/visualizations/public/components/visualization.test.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -jest.useFakeTimers(); - -import React from 'react'; -import { render, mount } from 'enzyme'; -import { Visualization } from './visualization'; - -let renderPromise; -class VisualizationStub { - constructor(el, vis) { - this.el = el; - this.vis = vis; - } - - render() { - renderPromise = new Promise((resolve) => { - this.el.innerText = this.vis.params.markdown; - resolve(); - }); - - return renderPromise; - } -} - -describe('', () => { - const visData = { - hits: 1, - }; - - const uiState = { - on: () => {}, - off: () => {}, - set: () => {}, - }; - - let vis; - - beforeEach(() => { - vis = { - setUiState: function (uiState) { - this.uiState = uiState; - }, - getUiState: function () { - return this.uiState; - }, - params: {}, - type: { - title: 'new vis', - requiresSearch: true, - useCustomNoDataScreen: false, - visualization: VisualizationStub, - }, - }; - }); - - it('should display no result message when length of data is 0', () => { - const data = { rows: [] }; - const wrapper = render( - - ); - expect(wrapper.text()).toBe('No results found'); - }); - - it('should render chart when data is present', () => { - const wrapper = render( - - ); - expect(wrapper.text()).not.toBe('No results found'); - }); - - it('should call onInit when rendering no data', () => { - const spy = jest.fn(); - const noData = { hits: 0 }; - mount( - - ); - expect(spy).toHaveBeenCalled(); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization.tsx b/src/plugins/visualizations/public/components/visualization.tsx deleted file mode 100644 index bdabe6bc07297..0000000000000 --- a/src/plugins/visualizations/public/components/visualization.tsx +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { get } from 'lodash'; -import React from 'react'; -import { PersistedState } from '../../../../plugins/visualizations/public'; -import { memoizeLast } from '../legacy/memoize'; -import { VisualizationChart } from './visualization_chart'; -import { VisualizationNoResults } from './visualization_noresults'; -import { ExprVis } from '../expressions/vis'; - -function shouldShowNoResultsMessage(vis: ExprVis, visData: any): boolean { - const requiresSearch = get(vis, 'type.requiresSearch'); - const rows: object[] | undefined = get(visData, 'rows'); - const isZeroHits = get(visData, 'hits') === 0 || (rows && !rows.length); - const shouldShowMessage = !get(vis, 'type.useCustomNoDataScreen'); - - return Boolean(requiresSearch && isZeroHits && shouldShowMessage); -} - -interface VisualizationProps { - listenOnChange: boolean; - onInit?: () => void; - uiState: PersistedState; - vis: ExprVis; - visData: any; - visParams: any; -} - -export class Visualization extends React.Component { - private showNoResultsMessage = memoizeLast(shouldShowNoResultsMessage); - - constructor(props: VisualizationProps) { - super(props); - - props.vis.setUiState(props.uiState); - } - - public render() { - const { vis, visData, visParams, onInit, uiState, listenOnChange } = this.props; - - const noResults = this.showNoResultsMessage(vis, visData); - - return ( -
- {noResults ? ( - - ) : ( - - )} -
- ); - } - - public shouldComponentUpdate(nextProps: VisualizationProps): boolean { - if (nextProps.uiState !== this.props.uiState) { - throw new Error('Changing uiState on is not supported!'); - } - return true; - } -} diff --git a/src/plugins/visualizations/public/components/visualization_chart.test.js b/src/plugins/visualizations/public/components/visualization_chart.test.js deleted file mode 100644 index 98cc5bb8d0729..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_chart.test.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -jest.useFakeTimers(); - -import React from 'react'; -import { render, mount } from 'enzyme'; -import { VisualizationChart } from './visualization_chart'; - -let renderPromise; - -class VisualizationStub { - constructor(el, vis) { - this.el = el; - this.vis = vis; - } - - render() { - renderPromise = new Promise((resolve) => { - this.el.textContent = this.vis.params.markdown; - resolve(); - }); - - return renderPromise; - } -} - -describe('', () => { - const vis = { - type: { - title: 'Test Visualization', - visualization: VisualizationStub, - }, - params: { - markdown: - 'This is a test of the [markdown](http://daringfireball.net/projects/markdown) vis.', - }, - }; - - it('should render initial html', () => { - const wrapper = render(); - expect(wrapper.text()).toBe(''); - }); - - it('should render visualization', async () => { - const wrapper = mount(); - jest.runAllTimers(); - await renderPromise; - expect(wrapper.find('.visChart').text()).toMatch(/markdown/); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization_chart.tsx b/src/plugins/visualizations/public/components/visualization_chart.tsx deleted file mode 100644 index c6ad1c53f91b7..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_chart.tsx +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import * as Rx from 'rxjs'; -import { debounceTime, filter, share, switchMap } from 'rxjs/operators'; -import { PersistedState } from '../../../../plugins/visualizations/public'; -import { VisualizationController } from '../types'; -import { ResizeChecker } from '../../../../plugins/kibana_utils/public'; -import { ExprVis } from '../expressions/vis'; - -interface VisualizationChartProps { - onInit?: () => void; - uiState: PersistedState; - vis: ExprVis; - visData: any; - visParams: any; - listenOnChange: boolean; -} - -class VisualizationChart extends React.Component { - private resizeChecker?: ResizeChecker; - private visualization?: VisualizationController; - private chartDiv = React.createRef(); - private containerDiv = React.createRef(); - private renderSubject: Rx.Subject<{ - vis: ExprVis; - visParams: any; - visData: any; - }>; - private renderSubscription: Rx.Subscription; - - constructor(props: VisualizationChartProps) { - super(props); - - this.renderSubject = new Rx.Subject(); - const render$ = this.renderSubject.asObservable().pipe(share()); - - const success$ = render$.pipe( - filter(({ vis, visData }) => vis && (!vis.type.requiresSearch || visData)), - debounceTime(100), - switchMap(async ({ vis, visData, visParams }) => { - if (!this.visualization) { - // This should never happen, since we only should trigger another rendering - // after this component has mounted and thus the visualization implementation - // has been initialized - throw new Error('Visualization implementation was not initialized on first render.'); - } - - return this.visualization.render(visData, visParams); - }) - ); - - this.renderSubscription = success$.subscribe(() => { - if (this.props.onInit) { - this.props.onInit(); - } - }); - } - - public render() { - return ( -
-
-
- ); - } - - public componentDidMount() { - if (!this.chartDiv.current || !this.containerDiv.current) { - throw new Error('chartDiv and currentDiv reference should always be present.'); - } - - const { vis } = this.props; - const Visualization = vis.type.visualization; - - if (!Visualization) { - throw new Error( - 'Tried to use VisualizationChart component with a vis without visualization property.' - ); - } - - this.visualization = new Visualization(this.chartDiv.current, vis); - - // We know that containerDiv.current will never be null, since we will always - // have rendered and the div is always rendered into the tree (i.e. not - // inside any condition). - this.resizeChecker = new ResizeChecker(this.containerDiv.current); - this.resizeChecker.on('resize', () => this.startRenderVisualization()); - - if (this.props.listenOnChange) { - this.props.uiState.on('change', this.onUiStateChanged); - } - - this.startRenderVisualization(); - } - - public componentDidUpdate() { - this.startRenderVisualization(); - } - - public componentWillUnmount() { - if (this.renderSubscription) { - this.renderSubscription.unsubscribe(); - } - if (this.resizeChecker) { - this.resizeChecker.destroy(); - } - if (this.visualization) { - this.visualization.destroy(); - } - } - - private onUiStateChanged = () => { - this.startRenderVisualization(); - }; - - private startRenderVisualization(): void { - if (this.containerDiv.current && this.chartDiv.current) { - this.renderSubject.next({ - vis: this.props.vis, - visData: this.props.visData, - visParams: this.props.visParams, - }); - } - } -} - -export { VisualizationChart }; diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.test.js b/src/plugins/visualizations/public/components/visualization_requesterror.test.js deleted file mode 100644 index 8a183efe3faa6..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_requesterror.test.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render } from 'enzyme'; -import { VisualizationRequestError } from './visualization_requesterror'; - -describe('VisualizationRequestError', () => { - it('should render according to snapshot', () => { - const wrapper = render(); - expect(wrapper).toMatchSnapshot(); - }); - - it('should set html when error is an object', () => { - const wrapper = render(); - expect(wrapper.text()).toBe('Request error'); - }); - - it('should set html when error is a string', () => { - const wrapper = render(); - expect(wrapper.text()).toBe('Request error'); - }); -}); diff --git a/src/plugins/visualizations/public/components/visualization_requesterror.tsx b/src/plugins/visualizations/public/components/visualization_requesterror.tsx deleted file mode 100644 index 3378875327959..0000000000000 --- a/src/plugins/visualizations/public/components/visualization_requesterror.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { EuiIcon, EuiSpacer, EuiText } from '@elastic/eui'; -import React from 'react'; -import { SearchError } from '../../../../plugins/data/public'; - -interface VisualizationRequestErrorProps { - onInit?: () => void; - error: SearchError | string; -} - -export class VisualizationRequestError extends React.Component { - private containerDiv = React.createRef(); - - public render() { - const { error } = this.props; - const errorMessage = typeof error === 'string' ? error : error.message; - - return ( -
- - - - - - {errorMessage} - -
- ); - } - - public componentDidMount() { - this.afterRender(); - } - - public componentDidUpdate() { - this.afterRender(); - } - - private afterRender() { - if (this.props.onInit) { - this.props.onInit(); - } - } -} diff --git a/src/plugins/visualizations/public/embeddable/_embeddables.scss b/src/plugins/visualizations/public/embeddable/_embeddables.scss index f0ec8479489d2..2a4d5c14a46a5 100644 --- a/src/plugins/visualizations/public/embeddable/_embeddables.scss +++ b/src/plugins/visualizations/public/embeddable/_embeddables.scss @@ -8,10 +8,6 @@ @include euiScrollBar; /* 2 */ } - .visualization .visChart__container { - overflow: visible; /* 1 */ - } - .visLegend__toggle { border-bottom-right-radius: 0; border-top-left-radius: 0; diff --git a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts b/src/plugins/visualizations/public/embeddable/get_index_pattern.ts deleted file mode 100644 index 65c6ebe75daf5..0000000000000 --- a/src/plugins/visualizations/public/embeddable/get_index_pattern.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { VisSavedObject } from '../types'; -import type { IndexPattern } from '../../../../plugins/data/public'; -import { getIndexPatterns } from '../services'; - -export async function getIndexPattern( - savedVis: VisSavedObject -): Promise { - if (savedVis.visState.type !== 'metrics') { - return savedVis.searchSource!.getField('index'); - } - - const indexPatternsClient = getIndexPatterns(); - - return savedVis.visState.params.index_pattern - ? (await indexPatternsClient.find(`"${savedVis.visState.params.index_pattern}"`))[0] - : await indexPatternsClient.getDefault(); -} diff --git a/src/plugins/visualizations/public/embeddable/to_ast.ts b/src/plugins/visualizations/public/embeddable/to_ast.ts new file mode 100644 index 0000000000000..31a9db5b92d11 --- /dev/null +++ b/src/plugins/visualizations/public/embeddable/to_ast.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ExpressionFunctionKibana, ExpressionFunctionKibanaContext } from '../../../data/public'; +import { buildExpression, buildExpressionFunction } from '../../../expressions/public'; + +import { VisToExpressionAst } from '../types'; + +/** + * Creates an ast expression for a visualization based on kibana context (query, filters, timerange) + * including a saved search if the visualization is based on it. + * The expression also includes particular visualization expression ast if presented. + * + * @internal + */ +export const toExpressionAst: VisToExpressionAst = async (vis, params) => { + const { savedSearchId, searchSource } = vis.data; + const query = searchSource?.getField('query'); + const filters = searchSource?.getField('filter'); + + const kibana = buildExpressionFunction('kibana', {}); + const kibanaContext = buildExpressionFunction('kibana_context', { + q: query && JSON.stringify(query), + filters: filters && JSON.stringify(filters), + savedSearchId, + }); + + const ast = buildExpression([kibana, kibanaContext]); + const expression = ast.toAst(); + + if (!vis.type.toExpressionAst) { + throw new Error('Visualization type definition should have toExpressionAst function defined'); + } + + const visExpressionAst = await vis.type.toExpressionAst(vis, params); + // expand the expression chain with a particular visualization expression chain, if it exists + expression.chain.push(...visExpressionAst.chain); + + return expression; +}; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index 88fc6e6a98c0d..45bd6a8a9d554 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -32,8 +32,8 @@ import { IExpressionLoaderParams, ExpressionsStart, ExpressionRenderError, + ExpressionAstExpression, } from '../../../../plugins/expressions/public'; -import { buildPipeline } from '../legacy/build_pipeline'; import { Vis, SerializedVis } from '../vis'; import { getExpressions, getUiActions } from '../services'; import { VIS_EVENT_TO_TRIGGER } from './events'; @@ -41,6 +41,7 @@ import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory'; import { SavedObjectAttributes } from '../../../../core/types'; import { SavedVisualizationsLoader } from '../saved_visualizations'; import { VisSavedObject } from '../types'; +import { toExpressionAst } from './to_ast'; const getKeys = (o: T): Array => Object.keys(o) as Array; @@ -94,7 +95,7 @@ export class VisualizeEmbeddable private syncColors?: boolean; private visCustomizations?: Pick; private subscriptions: Subscription[] = []; - private expression: string = ''; + private expression?: ExpressionAstExpression; private vis: Vis; private domNode: any; public readonly type = VISUALIZE_EMBEDDABLE_TYPE; @@ -382,7 +383,7 @@ export class VisualizeEmbeddable } this.abortController = new AbortController(); const abortController = this.abortController; - this.expression = await buildPipeline(this.vis, { + this.expression = await toExpressionAst(this.vis, { timefilter: this.timefilter, timeRange: this.timeRange, abortSignal: this.abortController!.signal, diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 399e0e4b71532..6a57bf7b4477a 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -74,12 +74,12 @@ export class VisualizeEmbeddableFactory type: 'visualization', getIconForSavedObject: (savedObject) => { return ( - getTypes().get(JSON.parse(savedObject.attributes.visState).type).icon || 'visualizeApp' + getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.icon || 'visualizeApp' ); }, getTooltipForSavedObject: (savedObject) => { return `${savedObject.attributes.title} (${ - getTypes().get(JSON.parse(savedObject.attributes.visState).type).title + getTypes().get(JSON.parse(savedObject.attributes.visState).type)?.title })`; }, showSavedObject: (savedObject) => { diff --git a/src/plugins/visualizations/public/expressions/vis.ts b/src/plugins/visualizations/public/expressions/vis.ts deleted file mode 100644 index a3952d284805d..0000000000000 --- a/src/plugins/visualizations/public/expressions/vis.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -/** - * @name Vis - * - * @description This class consists of aggs, params, listeners, title, and type. - * - Aggs: Instances of AggConfig. - * - Params: The settings in the Options tab. - * - * Not to be confused with vislib/vis.js. - */ - -import { EventEmitter } from 'events'; -import _ from 'lodash'; -import { VisParams, PersistedState } from '../../../../plugins/visualizations/public'; - -import { getTypes } from '../services'; -import { VisType } from '../vis_types'; - -export interface ExprVisState { - title?: string; - type: VisType | string; - params?: VisParams; -} - -export interface ExprVisAPIEvents { - filter: (data: any) => void; - brush: (data: any) => void; - applyFilter: (data: any) => void; -} - -export interface ExprVisAPI { - events: ExprVisAPIEvents; -} - -export class ExprVis extends EventEmitter { - public title: string = ''; - public type: VisType; - public params: VisParams = {}; - public sessionState: Record = {}; - public API: ExprVisAPI; - public eventsSubject: any; - private uiState: PersistedState; - - constructor(visState: ExprVisState = { type: 'histogram' }) { - super(); - - this.type = this.getType(visState.type); - this.uiState = new PersistedState(); - this.setState(visState); - - this.API = { - events: { - filter: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ - name: 'filterBucket', - data: data.data - ? { - data: data.data, - negate: data.negate, - } - : { data: [data] }, - }); - }, - brush: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'brush', data }); - }, - applyFilter: (data: any) => { - if (!this.eventsSubject) return; - this.eventsSubject.next({ name: 'applyFilter', data }); - }, - }, - }; - } - - private getType(type: string | VisType) { - if (_.isString(type)) { - const newType = getTypes().get(type); - if (!newType) { - throw new Error(`Invalid type "${type}"`); - } - return newType; - } else { - return type; - } - } - - setState(state: ExprVisState) { - this.title = state.title || ''; - if (state.type) { - this.type = this.getType(state.type); - } - this.params = _.defaultsDeep( - {}, - _.cloneDeep(state.params || {}), - _.cloneDeep(this.type.visConfig.defaults || {}) - ); - } - - getState() { - return { - title: this.title, - type: this.type.name, - params: _.cloneDeep(this.params), - }; - } - - updateState() { - this.emit('update'); - } - - forceReload() { - this.emit('reload'); - } - - isHierarchical() { - if (_.isFunction(this.type.hierarchicalData)) { - return !!this.type.hierarchicalData(this); - } else { - return !!this.type.hierarchicalData; - } - } - - hasUiState() { - return !!this.uiState; - } - - getUiState() { - return this.uiState; - } - - setUiState(state: PersistedState) { - this.uiState = state; - } - - /** - * Currently this is only used to extract map-specific information - * (e.g. mapZoom, mapCenter). - */ - uiStateVal(key: string, val: any) { - if (this.hasUiState()) { - if (_.isUndefined(val)) { - return this.uiState.get(key); - } - return this.uiState.set(key, val); - } - return val; - } -} diff --git a/src/plugins/visualizations/public/expressions/visualization_function.ts b/src/plugins/visualizations/public/expressions/visualization_function.ts deleted file mode 100644 index 623fb303baccc..0000000000000 --- a/src/plugins/visualizations/public/expressions/visualization_function.ts +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { get } from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { VisResponseValue, PersistedState } from '../../../../plugins/visualizations/public'; -import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public'; -import { getTypes, getIndexPatterns, getFilterManager, getSearch } from '../services'; - -interface Arguments { - index?: string | null; - metricsAtAllLevels?: boolean; - partialRows?: boolean; - type?: string; - schemas?: string; - visConfig?: string; - uiState?: string; - aggConfigs?: string; -} - -export type ExpressionFunctionVisualization = ExpressionFunctionDefinition< - 'visualization', - any, - Arguments, - Promise> ->; - -export const visualization = (): ExpressionFunctionVisualization => ({ - name: 'visualization', - type: 'render', - help: i18n.translate('visualizations.functions.visualization.help', { - defaultMessage: 'A simple visualization', - }), - args: { - // TODO: Below `help` keys should be internationalized once this function - // TODO: is moved to visualizations plugin. - index: { - types: ['string', 'null'], - default: null, - help: 'Index', - }, - metricsAtAllLevels: { - types: ['boolean'], - default: false, - help: 'Metrics levels', - }, - partialRows: { - types: ['boolean'], - default: false, - help: 'Partial rows', - }, - type: { - types: ['string'], - default: '', - help: 'Type', - }, - schemas: { - types: ['string'], - default: '"{}"', - help: 'Schemas', - }, - visConfig: { - types: ['string'], - default: '"{}"', - help: 'Visualization configuration', - }, - uiState: { - types: ['string'], - default: '"{}"', - help: 'User interface state', - }, - aggConfigs: { - types: ['string'], - default: '"{}"', - help: 'Aggregation configurations', - }, - }, - async fn(input, args, { inspectorAdapters }) { - const visConfigParams = args.visConfig ? JSON.parse(args.visConfig) : {}; - const schemas = args.schemas ? JSON.parse(args.schemas) : {}; - const visType = getTypes().get(args.type || 'histogram') as any; - const indexPattern = args.index ? await getIndexPatterns().get(args.index) : null; - - const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {}; - const uiState = new PersistedState(uiStateParams); - - const aggConfigsState = args.aggConfigs ? JSON.parse(args.aggConfigs) : []; - const aggs = indexPattern - ? getSearch().aggs.createAggConfigs(indexPattern, aggConfigsState) - : undefined; - - if (typeof visType.requestHandler === 'function') { - input = await visType.requestHandler({ - partialRows: args.partialRows, - metricsAtAllLevels: args.metricsAtAllLevels, - index: indexPattern, - visParams: visConfigParams, - timeRange: get(input, 'timeRange', null), - query: get(input, 'query', null), - filters: get(input, 'filters', null), - uiState, - inspectorAdapters, - queryFilter: getFilterManager(), - aggs, - }); - } - - if (typeof visType.responseHandler === 'function') { - if (input.columns) { - // assign schemas to aggConfigs - input.columns.forEach((column: any) => { - if (column.aggConfig) { - column.aggConfig.aggConfigs.schemas = visType.schemas.all; - } - }); - - Object.keys(schemas).forEach((key) => { - schemas[key].forEach((i: any) => { - if (input.columns[i] && input.columns[i].aggConfig) { - input.columns[i].aggConfig.schema = key; - } - }); - }); - } - - input = await visType.responseHandler(input, visConfigParams.dimensions); - } - - return { - type: 'render', - as: 'visualization', - value: { - visData: input, - visType: args.type || '', - visConfig: visConfigParams, - }, - }; - }, -}); diff --git a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx b/src/plugins/visualizations/public/expressions/visualization_renderer.tsx deleted file mode 100644 index 3547a5d7e20ce..0000000000000 --- a/src/plugins/visualizations/public/expressions/visualization_renderer.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -// @ts-ignore -import { ExprVis } from './vis'; -import { Visualization } from '../components'; -import { VisParams } from '../types'; - -export const visualization = () => ({ - name: 'visualization', - displayName: 'visualization', - reuseDomNode: true, - render: async (domNode: HTMLElement, config: any, handlers: any) => { - const { visData, visConfig, params } = config; - const visType = config.visType || visConfig.type; - - const vis = new ExprVis({ - title: config.title, - type: visType as string, - params: visConfig as VisParams, - }); - - vis.eventsSubject = { next: handlers.event }; - - const uiState = handlers.uiState || vis.getUiState(); - - handlers.onDestroy(() => { - unmountComponentAtNode(domNode); - }); - - const listenOnChange = params ? params.listenOnChange : false; - render( - , - domNode - ); - }, -}); diff --git a/src/plugins/visualizations/public/index.ts b/src/plugins/visualizations/public/index.ts index 0bf8aa6e5c418..b4216e1fc16af 100644 --- a/src/plugins/visualizations/public/index.ts +++ b/src/plugins/visualizations/public/index.ts @@ -10,7 +10,6 @@ import { PublicContract } from '@kbn/utility-types'; import { PluginInitializerContext } from 'src/core/public'; import { VisualizationsPlugin, VisualizationsSetup, VisualizationsStart } from './plugin'; import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable'; -import { ExprVis as ExprVisClass } from './expressions/vis'; export function plugin(initializerContext: PluginInitializerContext) { return new VisualizationsPlugin(initializerContext); @@ -20,39 +19,27 @@ export function plugin(initializerContext: PluginInitializerContext) { export { Vis } from './vis'; export { TypesService } from './vis_types/types_service'; export { VISUALIZE_EMBEDDABLE_TYPE, VIS_EVENT_TO_TRIGGER } from './embeddable'; -export { VisualizationContainer, VisualizationNoResults } from './components'; -export { getSchemas as getVisSchemas } from './legacy/build_pipeline'; +export { VisualizationContainer } from './components'; +export { getVisSchemas } from './vis_schemas'; /** @public types */ export { VisualizationsSetup, VisualizationsStart }; export { VisGroups } from './vis_types'; -export type { - VisTypeAlias, - VisType, - BaseVisTypeOptions, - ReactVisTypeOptions, - Schema, - ISchemas, -} from './vis_types'; +export type { VisTypeAlias, VisTypeDefinition, Schema, ISchemas } from './vis_types'; export { SerializedVis, SerializedVisData, VisData } from './vis'; export type VisualizeEmbeddableFactoryContract = PublicContract; export type VisualizeEmbeddableContract = PublicContract; export { VisualizeInput } from './embeddable'; -export type ExprVis = ExprVisClass; -export { SchemaConfig, BuildPipelineParams } from './legacy/build_pipeline'; -// @ts-ignore +export { SchemaConfig } from './vis_schemas'; export { updateOldState } from './legacy/vis_update_state'; export { PersistedState } from './persisted_state'; export { - VisualizationControllerConstructor, - VisualizationController, ISavedVis, VisSavedObject, - VisResponseValue, VisToExpressionAst, - VisParams, + VisToExpressionAstParams, + VisEditorOptionsProps, } from './types'; -export { ExprVisAPIEvents } from './expressions/vis'; export { VisualizationListItem, VisualizationStage } from './vis_types/vis_type_alias_registry'; export { VISUALIZE_ENABLE_LABS_SETTING } from '../common/constants'; -export { SavedVisState } from '../common'; +export { SavedVisState, VisParams } from '../common'; diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap deleted file mode 100644 index 3d685064111dc..0000000000000 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`visualize loader pipeline helpers: build pipeline buildPipeline calls toExpression on vis_type if it exists 1`] = `"kibana | kibana_context | test"`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts deleted file mode 100644 index a3d867380602d..0000000000000 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { prepareJson, prepareString, buildPipeline } from './build_pipeline'; -import { Vis } from '..'; -import { dataPluginMock } from '../../../../plugins/data/public/mocks'; -import { parseExpression } from '../../../expressions/common'; - -describe('visualize loader pipeline helpers: build pipeline', () => { - describe('prepareJson', () => { - it('returns a correctly formatted key/value string', () => { - const expected = `foo='{}' `; // trailing space is expected - const actual = prepareJson('foo', {}); - expect(actual).toBe(expected); - }); - - it('stringifies provided data', () => { - const expected = `foo='{\"well\":\"hello\",\"there\":{\"friend\":true}}' `; - const actual = prepareJson('foo', { well: 'hello', there: { friend: true } }); - expect(actual).toBe(expected); - }); - - it('escapes single quotes', () => { - const expected = `foo='{\"well\":\"hello \\'hi\\'\",\"there\":{\"friend\":true}}' `; - const actual = prepareJson('foo', { well: `hello 'hi'`, there: { friend: true } }); - expect(actual).toBe(expected); - }); - - it('returns empty string if data is undefined', () => { - const actual = prepareJson('foo', undefined); - expect(actual).toBe(''); - }); - }); - - describe('prepareString', () => { - it('returns a correctly formatted key/value string', () => { - const expected = `foo='bar' `; // trailing space is expected - const actual = prepareString('foo', 'bar'); - expect(actual).toBe(expected); - }); - - it('escapes single quotes', () => { - const expected = `foo='\\'bar\\'' `; - const actual = prepareString('foo', `'bar'`); - expect(actual).toBe(expected); - }); - - it('returns empty string if data is undefined', () => { - const actual = prepareString('foo', undefined); - expect(actual).toBe(''); - }); - }); - - describe('buildPipeline', () => { - const dataStart = dataPluginMock.createStartContract(); - - it('calls toExpression on vis_type if it exists', async () => { - const vis = ({ - getState: () => {}, - isHierarchical: () => false, - data: { - aggs: { - getResponseAggs: () => [], - }, - searchSource: { - getField: jest.fn(), - getParent: jest.fn(), - }, - }, - // @ts-ignore - type: { - toExpressionAst: () => parseExpression('test'), - }, - } as unknown) as Vis; - const expression = await buildPipeline(vis, { - timefilter: dataStart.query.timefilter.timefilter, - }); - expect(expression).toMatchSnapshot(); - }); - }); -}); diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts deleted file mode 100644 index d337ef7bcf379..0000000000000 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { - buildExpression, - formatExpression, - SerializedFieldFormat, -} from '../../../../plugins/expressions/public'; -import { IAggConfig, search, TimefilterContract } from '../../../../plugins/data/public'; -import { Vis } from '../types'; -const { isDateHistogramBucketAggConfig } = search.aggs; - -interface SchemaConfigParams { - precision?: number; - useGeocentroid?: boolean; -} - -export interface SchemaConfig { - accessor: number; - label: string; - format: SerializedFieldFormat; - params: SchemaConfigParams; - aggType: string; -} - -export interface Schemas { - metric: SchemaConfig[]; - bucket?: SchemaConfig[]; - geo_centroid?: any[]; - group?: any[]; - params?: any[]; - radius?: any[]; - segment?: any[]; - split_column?: SchemaConfig[]; - split_row?: SchemaConfig[]; - width?: any[]; - // catch all for schema name - [key: string]: any[] | undefined; -} -export interface BuildPipelineParams { - timefilter: TimefilterContract; - timeRange?: any; - abortSignal?: AbortSignal; -} - -export const getSchemas = ( - vis: Vis, - { timeRange, timefilter }: BuildPipelineParams -): Schemas => { - const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { - if (isDateHistogramBucketAggConfig(agg)) { - agg.params.timeRange = timeRange; - const bounds = - agg.params.timeRange && agg.fieldIsTimeField() - ? timefilter.calculateBounds(agg.params.timeRange) - : undefined; - agg.buckets.setBounds(bounds); - agg.buckets.setInterval(agg.params.interval); - } - - const hasSubAgg = [ - 'derivative', - 'moving_avg', - 'serial_diff', - 'cumulative_sum', - 'sum_bucket', - 'avg_bucket', - 'min_bucket', - 'max_bucket', - ].includes(agg.type.name); - - const formatAgg = hasSubAgg - ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg) - : agg; - - const params: SchemaConfigParams = {}; - - if (agg.type.name === 'geohash_grid') { - params.precision = agg.params.precision; - params.useGeocentroid = agg.params.useGeocentroid; - } - - const label = agg.makeLabel && agg.makeLabel(); - - return { - accessor, - format: formatAgg.toSerializedFieldFormat(), - params, - label, - aggType: agg.type.name, - }; - }; - - let cnt = 0; - const schemas: Schemas = { - metric: [], - }; - - if (!vis.data.aggs) { - return schemas; - } - - const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); - const isHierarchical = vis.isHierarchical(); - const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics'); - responseAggs.forEach((agg: IAggConfig) => { - let skipMetrics = false; - let schemaName = agg.schema; - if (!schemaName) { - if (agg.type.name === 'geo_centroid') { - schemaName = 'geo_centroid'; - } else { - cnt++; - return; - } - } - if (schemaName === 'split') { - // TODO: We should check if there's a better way then casting to `any` here - schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`; - skipMetrics = responseAggs.length - metrics.length > 1; - } - if (!schemas[schemaName]) { - schemas[schemaName] = []; - } - if (!isHierarchical || agg.type.type !== 'metrics') { - schemas[schemaName]!.push(createSchemaConfig(cnt++, agg)); - } - if (isHierarchical && (agg.type.type !== 'metrics' || metrics.length === responseAggs.length)) { - metrics.forEach((metric: any) => { - const schemaConfig = createSchemaConfig(cnt++, metric); - if (!skipMetrics) { - schemas.metric.push(schemaConfig); - } - }); - } - }); - return schemas; -}; - -export const prepareJson = (variable: string, data?: object): string => { - if (data === undefined) { - return ''; - } - return `${variable}='${JSON.stringify(data).replace(/\\/g, `\\\\`).replace(/'/g, `\\'`)}' `; -}; - -export const escapeString = (data: string): string => { - return data.replace(/\\/g, `\\\\`).replace(/'/g, `\\'`); -}; - -export const prepareString = (variable: string, data?: string): string => { - if (data === undefined) { - return ''; - } - return `${variable}='${escapeString(data)}' `; -}; - -export const prepareValue = (variable: string, data: any, raw: boolean = false) => { - if (data === undefined) { - return ''; - } - if (raw) { - return `${variable}=${data} `; - } - switch (typeof data) { - case 'string': - return prepareString(variable, data); - case 'object': - return prepareJson(variable, data); - default: - return `${variable}=${data} `; - } -}; - -export const prepareDimension = (variable: string, data: any) => { - if (data === undefined) { - return ''; - } - - let expr = `${variable}={visdimension ${data.accessor} `; - if (data.format) { - expr += prepareValue('format', data.format.id); - expr += prepareJson('formatParams', data.format.params); - } - expr += '} '; - - return expr; -}; - -export const buildPipeline = async (vis: Vis, params: BuildPipelineParams) => { - const { indexPattern, searchSource } = vis.data; - const query = searchSource!.getField('query'); - const filters = searchSource!.getField('filter'); - const { uiState, title } = vis; - - // context - let pipeline = `kibana | kibana_context `; - if (query) { - pipeline += prepareJson('query', query); - } - if (filters) { - pipeline += prepareJson('filters', filters); - } - if (vis.data.savedSearchId) { - pipeline += prepareString('savedSearchId', vis.data.savedSearchId); - } - pipeline += '| '; - - if (vis.type.toExpressionAst) { - const visAst = await vis.type.toExpressionAst(vis, params); - pipeline += formatExpression(visAst); - } else { - // request handler - if (vis.type.requestHandler === 'courier') { - pipeline += `esaggs - index={indexPatternLoad ${prepareString('id', indexPattern!.id)}} - metricsAtAllLevels=${vis.isHierarchical()} - partialRows=${vis.params.showPartialRows || false} `; - if (vis.data.aggs) { - vis.data.aggs.aggs.forEach((agg) => { - const ast = agg.toExpressionAst(); - if (ast) { - pipeline += `aggs={${buildExpression(ast).toString()}} `; - } - }); - } - pipeline += `| `; - } else { - const schemas = getSchemas(vis, params); - const visConfig = { ...vis.params }; - visConfig.dimensions = schemas; - visConfig.title = title; - pipeline += `visualization type='${vis.type.name}' - ${prepareJson('visConfig', visConfig)} - ${prepareJson('uiState', uiState)} - metricsAtAllLevels=${vis.isHierarchical()} - partialRows=${vis.params.showPartialRows || false} `; - if (indexPattern) { - pipeline += `${prepareString('index', indexPattern.id)} `; - if (vis.data.aggs) { - pipeline += `${prepareJson('aggConfigs', vis.data.aggs!.aggs)}`; - } - } - } - } - - return pipeline; -}; diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts index a8bf3b1ff3eb9..4f9fd53125847 100644 --- a/src/plugins/visualizations/public/mocks.ts +++ b/src/plugins/visualizations/public/mocks.ts @@ -22,7 +22,6 @@ import { savedObjectsPluginMock } from '../../../plugins/saved_objects/public/mo const createSetupContract = (): VisualizationsSetup => ({ createBaseVisualization: jest.fn(), - createReactVisualization: jest.fn(), registerAlias: jest.fn(), hideTypes: jest.fn(), }); diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 8a82b36f37caf..24514dde10cda 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -20,15 +20,12 @@ import { TypesService, TypesSetup, TypesStart } from './vis_types'; import { setUISettings, setTypes, - setI18n, setApplication, setCapabilities, setHttp, - setIndexPatterns, setSearch, setSavedObjects, setUsageCollector, - setFilterManager, setExpressions, setUiActions, setSavedVisualizationsLoader, @@ -47,8 +44,6 @@ import { } from './embeddable'; import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public'; import { EmbeddableSetup, EmbeddableStart } from '../../embeddable/public'; -import { visualization as visualizationFunction } from './expressions/visualization_function'; -import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; import { range as rangeExpressionFunction } from './expression_functions/range'; import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension'; import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public'; @@ -138,8 +133,6 @@ export class VisualizationsPlugin setUISettings(core.uiSettings); setUsageCollector(usageCollection); - expressions.registerFunction(visualizationFunction); - expressions.registerRenderer(visualizationRenderer); expressions.registerFunction(rangeExpressionFunction); expressions.registerFunction(visDimensionExpressionFunction); const embeddableFactory = new VisualizeEmbeddableFactory({ start }); @@ -155,7 +148,6 @@ export class VisualizationsPlugin { data, expressions, uiActions, embeddable, dashboard, savedObjects }: VisualizationsStartDeps ): VisualizationsStart { const types = this.types.start(); - setI18n(core.i18n); setTypes(types); setEmbeddable(embeddable); setApplication(core.application); @@ -163,9 +155,7 @@ export class VisualizationsPlugin setHttp(core.http); setSavedObjects(core.savedObjects); setDocLinks(core.docLinks); - setIndexPatterns(data.indexPatterns); setSearch(data.search); - setFilterManager(data.query.filterManager); setExpressions(expressions); setUiActions(uiActions); setTimeFilter(data.query.timefilter.timefilter); diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index dd7d0941e7378..30cc514e48de4 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -11,7 +11,6 @@ import { Capabilities, ChromeStart, HttpStart, - I18nStart, IUiSettingsClient, OverlayStart, SavedObjectsStart, @@ -19,12 +18,7 @@ import { } from '../../../core/public'; import { TypesStart } from './vis_types'; import { createGetterSetter } from '../../../plugins/kibana_utils/public'; -import { - DataPublicPluginStart, - FilterManager, - IndexPatternsContract, - TimefilterContract, -} from '../../../plugins/data/public'; +import { DataPublicPluginStart, TimefilterContract } from '../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../plugins/usage_collection/public'; import { ExpressionsStart } from '../../../plugins/expressions/public'; import { UiActionsStart } from '../../../plugins/ui_actions/public'; @@ -48,20 +42,10 @@ export const [getSavedObjects, setSavedObjects] = createGetterSetter('Types'); -export const [getI18n, setI18n] = createGetterSetter('I18n'); - export const [getDocLinks, setDocLinks] = createGetterSetter('DocLinks'); -export const [getFilterManager, setFilterManager] = createGetterSetter( - 'FilterManager' -); - export const [getTimeFilter, setTimeFilter] = createGetterSetter('TimeFilter'); -export const [getIndexPatterns, setIndexPatterns] = createGetterSetter( - 'IndexPatterns' -); - export const [getSearch, setSearch] = createGetterSetter('Search'); export const [getUsageCollector, setUsageCollector] = createGetterSetter( diff --git a/src/plugins/visualizations/public/types.ts b/src/plugins/visualizations/public/types.ts index dc9ca49840561..3a751c92f3ae7 100644 --- a/src/plugins/visualizations/public/types.ts +++ b/src/plugins/visualizations/public/types.ts @@ -7,26 +7,27 @@ */ import { SavedObject } from '../../../plugins/saved_objects/public'; -import { SearchSourceFields, TimefilterContract } from '../../../plugins/data/public'; +import { + AggConfigOptions, + IAggConfigs, + SearchSourceFields, + TimefilterContract, +} from '../../../plugins/data/public'; import { ExpressionAstExpression } from '../../expressions/public'; import { SerializedVis, Vis } from './vis'; -import { ExprVis } from './expressions/vis'; -import { SavedVisState, VisParams } from '../common/types'; +import { PersistedState } from './persisted_state'; +import { VisParams } from '../common'; export { Vis, SerializedVis, VisParams }; -export interface VisualizationController { - render(visData: any, visParams: any): Promise; - destroy(): void; - isLoaded?(): Promise | void; +export interface SavedVisState { + title: string; + type: string; + params: VisParams; + aggs: AggConfigOptions[]; } -export type VisualizationControllerConstructor = new ( - el: HTMLElement, - vis: ExprVis -) => VisualizationController; - export interface ISavedVis { id?: string; title: string; @@ -40,13 +41,6 @@ export interface ISavedVis { export interface VisSavedObject extends SavedObject, ISavedVis {} -export interface VisResponseValue { - visType: string; - visData: object; - visConfig: object; - params?: object; -} - export interface VisToExpressionAstParams { timefilter: TimefilterContract; timeRange?: any; @@ -57,3 +51,15 @@ export type VisToExpressionAst = ( vis: Vis, params: VisToExpressionAstParams ) => Promise | ExpressionAstExpression; + +export interface VisEditorOptionsProps { + aggs: IAggConfigs; + hasHistogramAgg: boolean; + isTabSelected: boolean; + stateParams: VisParamType; + vis: Vis; + uiState: PersistedState; + setValue(paramName: T, value: VisParamType[T]): void; + setValidity(isValid: boolean): void; + setTouched(isTouched: boolean): void; +} diff --git a/src/plugins/visualizations/public/vis.ts b/src/plugins/visualizations/public/vis.ts index 56a151fb82ed3..ab44c361bd5bd 100644 --- a/src/plugins/visualizations/public/vis.ts +++ b/src/plugins/visualizations/public/vis.ts @@ -22,7 +22,6 @@ import { i18n } from '@kbn/i18n'; import { PersistedState } from './persisted_state'; import { getTypes, getAggs, getSearch, getSavedSearchLoader } from './services'; -import { VisType } from './vis_types'; import { IAggConfigs, IndexPattern, @@ -30,6 +29,7 @@ import { AggConfigOptions, SearchSourceFields, } from '../../../plugins/data/public'; +import { BaseVisType } from './vis_types'; import { VisParams } from '../common/types'; export interface SerializedVisData { @@ -71,14 +71,11 @@ const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: type PartialVisState = Assign }>; export class Vis { - public readonly type: VisType; + public readonly type: BaseVisType; public readonly id?: string; public title: string = ''; public description: string = ''; public params: TVisParams; - // Session state is for storing information that is transitory, and will not be saved with the visualization. - // For instance, map bounds, which depends on the view port, browser window size, etc. - public sessionState: Record = {}; public data: VisData = {}; public readonly uiState: PersistedState; diff --git a/src/plugins/visualizations/public/vis_schemas.ts b/src/plugins/visualizations/public/vis_schemas.ts new file mode 100644 index 0000000000000..a00417b90baac --- /dev/null +++ b/src/plugins/visualizations/public/vis_schemas.ts @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { SerializedFieldFormat } from '../../expressions/public'; +import { IAggConfig, search } from '../../data/public'; + +import { Vis, VisToExpressionAstParams } from './types'; + +const { isDateHistogramBucketAggConfig } = search.aggs; + +interface SchemaConfigParams { + precision?: number; + useGeocentroid?: boolean; +} + +export interface SchemaConfig { + accessor: number; + label: string; + format: SerializedFieldFormat; + params: SchemaConfigParams; + aggType: string; +} + +export interface Schemas { + metric: SchemaConfig[]; + bucket?: SchemaConfig[]; + geo_centroid?: any[]; + group?: any[]; + params?: any[]; + radius?: any[]; + segment?: any[]; + split_column?: SchemaConfig[]; + split_row?: SchemaConfig[]; + width?: any[]; + // catch all for schema name + [key: string]: any[] | undefined; +} + +export const getVisSchemas = ( + vis: Vis, + { timeRange, timefilter }: VisToExpressionAstParams +): Schemas => { + const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { + if (isDateHistogramBucketAggConfig(agg)) { + agg.params.timeRange = timeRange; + const bounds = + agg.params.timeRange && agg.fieldIsTimeField() + ? timefilter.calculateBounds(agg.params.timeRange) + : undefined; + agg.buckets.setBounds(bounds); + agg.buckets.setInterval(agg.params.interval); + } + + const hasSubAgg = [ + 'derivative', + 'moving_avg', + 'serial_diff', + 'cumulative_sum', + 'sum_bucket', + 'avg_bucket', + 'min_bucket', + 'max_bucket', + ].includes(agg.type.name); + + const formatAgg = hasSubAgg + ? agg.params.customMetric || agg.aggConfigs.getRequestAggById(agg.params.metricAgg) + : agg; + + const params: SchemaConfigParams = {}; + + if (agg.type.name === 'geohash_grid') { + params.precision = agg.params.precision; + params.useGeocentroid = agg.params.useGeocentroid; + } + + const label = agg.makeLabel && agg.makeLabel(); + + return { + accessor, + format: formatAgg.toSerializedFieldFormat(), + params, + label, + aggType: agg.type.name, + }; + }; + + let cnt = 0; + const schemas: Schemas = { + metric: [], + }; + + if (!vis.data.aggs) { + return schemas; + } + + const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled); + const isHierarchical = vis.isHierarchical(); + const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics'); + responseAggs.forEach((agg: IAggConfig) => { + let skipMetrics = false; + let schemaName = agg.schema; + if (!schemaName) { + if (agg.type.name === 'geo_centroid') { + schemaName = 'geo_centroid'; + } else { + cnt++; + return; + } + } + if (schemaName === 'split') { + // TODO: We should check if there's a better way then casting to `any` here + schemaName = `split_${(vis.params as any).row ? 'row' : 'column'}`; + skipMetrics = responseAggs.length - metrics.length > 1; + } + if (!schemas[schemaName]) { + schemas[schemaName] = []; + } + if (!isHierarchical || agg.type.type !== 'metrics') { + schemas[schemaName]!.push(createSchemaConfig(cnt++, agg)); + } + if (isHierarchical && (agg.type.type !== 'metrics' || metrics.length === responseAggs.length)) { + metrics.forEach((metric: any) => { + const schemaConfig = createSchemaConfig(cnt++, metric); + if (!skipMetrics) { + schemas.metric.push(schemaConfig); + } + }); + } + }); + return schemas; +}; diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts index 58670545f53e7..5091f9aca6b69 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.test.ts @@ -16,7 +16,16 @@ describe('BaseVisType', () => { name: 'test', title: 'test', description: 'test', - visualization: {} as any, + visConfig: { + defaults: {}, + }, + toExpressionAst: () => ({ + type: 'expression', + chain: [], + }), + editorConfig: { + editor: 'custom', + }, }); }).toThrow(); }); diff --git a/src/plugins/visualizations/public/vis_types/base_vis_type.ts b/src/plugins/visualizations/public/vis_types/base_vis_type.ts index e6e1704d45a8e..7425bf06bdfc2 100644 --- a/src/plugins/visualizations/public/vis_types/base_vis_type.ts +++ b/src/plugins/visualizations/public/vis_types/base_vis_type.ts @@ -9,54 +9,9 @@ import { defaultsDeep } from 'lodash'; import { VisParams } from '../types'; -import { VisType, VisTypeOptions, VisGroups } from './types'; +import { VisTypeDefinition, VisTypeOptions, VisGroups } from './types'; import { Schemas } from './schemas'; -interface CommonBaseVisTypeOptions - extends Pick< - VisType, - | 'description' - | 'getInfoMessage' - | 'getSupportedTriggers' - | 'hierarchicalData' - | 'icon' - | 'image' - | 'inspectorAdapters' - | 'name' - | 'requestHandler' - | 'responseHandler' - | 'setup' - | 'title' - >, - Pick< - Partial>, - | 'editorConfig' - | 'hidden' - | 'stage' - | 'getUsedIndexPattern' - | 'useCustomNoDataScreen' - | 'visConfig' - | 'group' - | 'titleInWizard' - | 'note' - > { - options?: Partial['options']>; -} - -interface ExpressionBaseVisTypeOptions extends CommonBaseVisTypeOptions { - toExpressionAst: VisType['toExpressionAst']; - visualization?: undefined; -} - -interface VisualizationBaseVisTypeOptions extends CommonBaseVisTypeOptions { - toExpressionAst?: undefined; - visualization: VisType['visualization']; -} - -export type BaseVisTypeOptions = - | ExpressionBaseVisTypeOptions - | VisualizationBaseVisTypeOptions; - const defaultOptions: VisTypeOptions = { showTimePicker: true, showQueryBar: true, @@ -65,7 +20,7 @@ const defaultOptions: VisTypeOptions = { hierarchicalData: false, // we should get rid of this i guess ? }; -export class BaseVisType implements VisType { +export class BaseVisType { public readonly name; public readonly title; public readonly description; @@ -76,23 +31,20 @@ export class BaseVisType implements VisType public readonly stage; public readonly group; public readonly titleInWizard; - public readonly options; - public readonly visualization; + public readonly options: VisTypeOptions; public readonly visConfig; public readonly editorConfig; public hidden; - public readonly requestHandler; - public readonly responseHandler; + public readonly requiresSearch; public readonly hierarchicalData; public readonly setup; public readonly getUsedIndexPattern; - public readonly useCustomNoDataScreen; public readonly inspectorAdapters; public readonly toExpressionAst; public readonly getInfoMessage; public readonly schemas; - constructor(opts: BaseVisTypeOptions) { + constructor(opts: VisTypeDefinition) { if (!opts.icon && !opts.image) { throw new Error('vis_type must define its icon or image'); } @@ -104,7 +56,6 @@ export class BaseVisType implements VisType this.title = opts.title; this.icon = opts.icon; this.image = opts.image; - this.visualization = opts.visualization; this.visConfig = defaultsDeep({}, opts.visConfig, { defaults: {} }); this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} }); this.options = defaultsDeep({}, opts.options, defaultOptions); @@ -112,20 +63,14 @@ export class BaseVisType implements VisType this.group = opts.group ?? VisGroups.AGGBASED; this.titleInWizard = opts.titleInWizard ?? ''; this.hidden = opts.hidden ?? false; - this.requestHandler = opts.requestHandler ?? 'courier'; - this.responseHandler = opts.responseHandler ?? 'none'; + this.requiresSearch = opts.requiresSearch ?? false; this.setup = opts.setup; this.hierarchicalData = opts.hierarchicalData ?? false; this.getUsedIndexPattern = opts.getUsedIndexPattern; - this.useCustomNoDataScreen = opts.useCustomNoDataScreen ?? false; this.inspectorAdapters = opts.inspectorAdapters; this.toExpressionAst = opts.toExpressionAst; this.getInfoMessage = opts.getInfoMessage; this.schemas = new Schemas(this.editorConfig?.schemas ?? []); } - - public get requiresSearch(): boolean { - return this.requestHandler !== 'none'; - } } diff --git a/src/plugins/visualizations/public/vis_types/index.ts b/src/plugins/visualizations/public/vis_types/index.ts index 0b3b1ec75cbe3..021c55e7a14bc 100644 --- a/src/plugins/visualizations/public/vis_types/index.ts +++ b/src/plugins/visualizations/public/vis_types/index.ts @@ -9,6 +9,5 @@ export * from './types_service'; export { Schemas } from './schemas'; export { VisGroups } from './types'; -export type { VisType, ISchemas, Schema } from './types'; -export type { BaseVisTypeOptions } from './base_vis_type'; -export type { ReactVisTypeOptions } from './react_vis_type'; +export { BaseVisType } from './base_vis_type'; +export type { VisTypeDefinition, ISchemas, Schema } from './types'; diff --git a/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx b/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx deleted file mode 100644 index 91e8db947f7e4..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_controller.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { VisualizationController } from '../types'; -import { getI18n, getUISettings } from '../services'; -import { ExprVis } from '../expressions/vis'; - -export class ReactVisController implements VisualizationController { - constructor(private element: HTMLElement, private vis: ExprVis) {} - - public render(visData: any, visParams: any): Promise { - const I18nContext = getI18n().Context; - - return new Promise((resolve, reject) => { - if (!this.vis.type || !this.vis.type.visConfig || !this.vis.type.visConfig.component) { - reject('Missing component for ReactVisType'); - } - - const Component = this.vis.type.visConfig.component; - const config = getUISettings(); - render( - - - , - this.element - ); - }); - } - - public destroy() { - unmountComponentAtNode(this.element); - } -} diff --git a/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts deleted file mode 100644 index 76d3321f2c92d..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_type.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { cloneDeep } from 'lodash'; -import { ReactVisType } from './react_vis_type'; - -describe('React Vis Type', () => { - const visConfig = { - name: 'test', - title: 'test', - description: 'test', - icon: 'test', - visConfig: { component: 'test' }, - }; - - describe('initialization', () => { - it('should throw if component is not set', () => { - expect(() => { - const missingConfig = cloneDeep(visConfig); - // @ts-expect-error TS knows it's a required property - delete missingConfig.visConfig.component; - new ReactVisType(missingConfig); - }).toThrow(); - }); - - it('creates react controller', () => { - const visType = new ReactVisType(visConfig); - expect(visType.visualization).not.toBeUndefined(); - }); - }); -}); diff --git a/src/plugins/visualizations/public/vis_types/react_vis_type.ts b/src/plugins/visualizations/public/vis_types/react_vis_type.ts deleted file mode 100644 index 336599f67702d..0000000000000 --- a/src/plugins/visualizations/public/vis_types/react_vis_type.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { BaseVisType, BaseVisTypeOptions } from './base_vis_type'; -import { ReactVisController } from './react_vis_controller'; -import { VisType } from './types'; - -export type ReactVisTypeOptions = Omit< - BaseVisTypeOptions, - 'visualization' | 'toExpressionAst' ->; - -/** - * This class should only be used for visualizations not using the `toExpressionAst` with a custom renderer. - * If you implement a custom renderer you should just mount a react component inside this. - */ -export class ReactVisType - extends BaseVisType - implements VisType { - constructor(opts: ReactVisTypeOptions) { - super({ - ...opts, - visualization: ReactVisController, - }); - - if (!this.visConfig.component) { - throw new Error('Missing component for ReactVisType'); - } - } -} diff --git a/src/plugins/visualizations/public/vis_types/schemas.ts b/src/plugins/visualizations/public/vis_types/schemas.ts index 3ce38e68e7f32..50bf50a857815 100644 --- a/src/plugins/visualizations/public/vis_types/schemas.ts +++ b/src/plugins/visualizations/public/vis_types/schemas.ts @@ -37,7 +37,6 @@ export class Schemas implements ISchemas { group: AggGroupNames.Buckets, title: schema.name, aggFilter: '*', - editor: false, params: [], }); diff --git a/src/plugins/visualizations/public/vis_types/types.ts b/src/plugins/visualizations/public/vis_types/types.ts index b38c9e41d5c7b..7244aae64e963 100644 --- a/src/plugins/visualizations/public/vis_types/types.ts +++ b/src/plugins/visualizations/public/vis_types/types.ts @@ -7,10 +7,10 @@ */ import { IconType } from '@elastic/eui'; -import React, { ReactNode } from 'react'; +import { ReactNode } from 'react'; import { Adapters } from 'src/plugins/inspector'; import { IndexPattern, AggGroupNames, AggParam, AggGroupName } from '../../../data/public'; -import { Vis, VisParams, VisToExpressionAst, VisualizationControllerConstructor } from '../types'; +import { Vis, VisEditorOptionsProps, VisParams, VisToExpressionAst } from '../types'; export interface VisTypeOptions { showTimePicker: boolean; @@ -34,7 +34,6 @@ export interface ISchemas { export interface Schema { aggFilter: string[]; - editor: boolean | string; group: AggGroupName; max: number; min: number; @@ -49,11 +48,35 @@ export interface Schema { tooltip?: ReactNode; } +type DefaultEditorOptionsComponent = React.ComponentType< + VisEditorOptionsProps +>; + +interface DefaultEditorConfig { + // collections should moved directly into default editor in https://github.com/elastic/kibana/issues/84879 + collections?: { + [key: string]: Array<{ text: string; value: string }> | Array<{ id: string; label: string }>; + }; + enableAutoApply?: boolean; + defaultSize?: string; + optionsTemplate?: DefaultEditorOptionsComponent; + optionTabs?: Array<{ + name: string; + title: string; + editor: DefaultEditorOptionsComponent; + }>; + schemas?: Array>; +} + +interface CustomEditorConfig { + editor: string; +} + /** - * A visualization type representing one specific type of "classical" + * A visualization type definition representing a spec of one specific type of "classical" * visualizations (i.e. not Lens visualizations). */ -export interface VisType { +export interface VisTypeDefinition { /** * Visualization unique name */ @@ -69,7 +92,7 @@ export interface VisType { /** * If given, it will be diplayed on the wizard vis card as a note in italic. */ - readonly note: string; + readonly note?: string; /** * If given, it will return the supported triggers for this vis. */ @@ -82,8 +105,6 @@ export interface VisType { readonly getUsedIndexPattern?: (visParams: VisParams) => IndexPattern[] | Promise; readonly isAccessible?: boolean; - readonly requestHandler?: string | unknown; - readonly responseHandler?: string | unknown; /** * It is the visualization icon, displayed on the wizard. */ @@ -94,21 +115,27 @@ export interface VisType { readonly image?: string; /** * Describes the visualization stage + * @default 'production' */ - readonly stage: 'experimental' | 'beta' | 'production'; + readonly stage?: 'experimental' | 'beta' | 'production'; /** * Describes the experience group that the visualization belongs. * It can be on tools, aggregation based or promoted group. + * @default 'aggbased' */ - readonly group: VisGroups; + readonly group?: VisGroups; /** * If given, it will be displayed on the wizard instead of the title. * We use it because we want to differentiate the vis title from the * way it is presented on the wizard */ - readonly titleInWizard: string; - readonly requiresSearch: boolean; - readonly useCustomNoDataScreen: boolean; + readonly titleInWizard?: string; + /** + * The flag is necessary for aggregation based visualizations. + * When "true", an additional step on the vis creation wizard will be provided + * with the selection of a search source - an index pattern or a saved search. + */ + readonly requiresSearch?: boolean; readonly hierarchicalData?: boolean | ((vis: { params: TVisParams }) => boolean); readonly inspectorAdapters?: Adapters | (() => Adapters); /** @@ -118,18 +145,27 @@ export interface VisType { * of this type. */ readonly getInfoMessage?: (vis: Vis) => React.ReactNode; - - readonly toExpressionAst?: VisToExpressionAst; - readonly visualization?: VisualizationControllerConstructor; + /** + * Should be provided to expand base visualization expression with + * custom exprsesion chain, including render expression. + * Explicit renderer should be registered in expressions plugin to render your visualization. + */ + readonly toExpressionAst: VisToExpressionAst; readonly setup?: (vis: Vis) => Promise>; - hidden: boolean; + hidden?: boolean; - readonly schemas: ISchemas; + readonly options?: Partial; - readonly options: VisTypeOptions; - - // TODO: The following types still need to be refined properly. - readonly editorConfig: Record; + /** + * Config for the default editor. + * Custom editor can be specified. + */ + readonly editorConfig: DefaultEditorConfig | CustomEditorConfig; + /** + * Have the "defaults" prop with default params for a visualization. + * TODO: ideally should have next type: { defaults: TVisParams } , but currently + * have incosistencies in legacy visLib visualizations + */ readonly visConfig: Record; } diff --git a/src/plugins/visualizations/public/vis_types/types_service.ts b/src/plugins/visualizations/public/vis_types/types_service.ts index d26f5ca882b65..09f7f8f599f2c 100644 --- a/src/plugins/visualizations/public/vis_types/types_service.ts +++ b/src/plugins/visualizations/public/vis_types/types_service.ts @@ -7,9 +7,8 @@ */ import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry'; -import { BaseVisType, BaseVisTypeOptions } from './base_vis_type'; -import { ReactVisType, ReactVisTypeOptions } from './react_vis_type'; -import { VisType, VisGroups } from './types'; +import { BaseVisType } from './base_vis_type'; +import { VisTypeDefinition, VisGroups } from './types'; /** * Vis Types Service @@ -17,10 +16,10 @@ import { VisType, VisGroups } from './types'; * @internal */ export class TypesService { - private types: Record> = {}; + private types: Record> = {}; private unregisteredHiddenTypes: string[] = []; - private registerVisualization(visDefinition: VisType) { + private registerVisualization(visDefinition: BaseVisType) { if (this.unregisteredHiddenTypes.includes(visDefinition.name)) { visDefinition.hidden = true; } @@ -37,18 +36,10 @@ export class TypesService { * registers a visualization type * @param config - visualization type definition */ - createBaseVisualization: (config: BaseVisTypeOptions): void => { + createBaseVisualization: (config: VisTypeDefinition): void => { const vis = new BaseVisType(config); this.registerVisualization(vis); }, - /** - * registers a visualization which uses react for rendering - * @param config - visualization type definition - */ - createReactVisualization: (config: ReactVisTypeOptions): void => { - const vis = new ReactVisType(config); - this.registerVisualization(vis); - }, /** * registers a visualization alias * alias is a visualization type without implementation, it just redirects somewhere in kibana @@ -77,13 +68,13 @@ export class TypesService { * returns specific visualization or undefined if not found * @param {string} visualization - id of visualization to return */ - get: (visualization: string): VisType => { + get: (visualization: string): BaseVisType | undefined => { return this.types[visualization]; }, /** * returns all registered visualization types */ - all: (): VisType[] => { + all: (): BaseVisType[] => { return [...Object.values(this.types)]; }, /** @@ -119,6 +110,3 @@ export type TypesStart = ReturnType; /** @public types */ export { VisTypeAlias }; - -/** @public static code */ -// TODO once items are moved from ui/vis into this service diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx index 053d36e89ad75..9e825982b9065 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.test.tsx @@ -8,19 +8,13 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../../vis_types'; +import { TypesStart, BaseVisType, VisGroups } from '../../vis_types'; import { AggBasedSelection } from './agg_based_selection'; describe('AggBasedSelection', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -50,22 +44,16 @@ describe('AggBasedSelection', () => { stage: 'production', ...defaultVisTypeParams, }, - ] as VisType[]; + ] as BaseVisType[]; const visTypes: TypesStart = { - get(id: string): VisType { - return _visTypes.find((vis) => vis.name === id) as VisType; - }, - all: () => { - return (_visTypes as unknown) as VisType[]; + get(id: string): BaseVisType { + return (_visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, + all: () => _visTypes, getAliases: () => [], unRegisterAlias: () => [], - getByGroup: (group: VisGroups) => { - return _visTypes.filter((type) => { - return type.group === group; - }) as VisType[]; - }, + getByGroup: (group: VisGroups) => _visTypes.filter((type) => type.group === group), }; beforeAll(() => { diff --git a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx index 1496f0c60f82d..f5013dddc5c79 100644 --- a/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx +++ b/src/plugins/visualizations/public/wizard/agg_based_selection/agg_based_selection.tsx @@ -25,17 +25,17 @@ import { } from '@elastic/eui'; import { memoizeLast } from '../../legacy/memoize'; -import type { VisType, TypesStart } from '../../vis_types'; +import type { BaseVisType, TypesStart } from '../../vis_types'; import { VisGroups } from '../../vis_types'; import { DialogNavigation } from '../dialog_navigation'; interface VisTypeListEntry { - type: VisType; + type: BaseVisType; highlighted: boolean; } interface AggBasedSelectionProps { - onVisTypeSelected: (visType: VisType) => void; + onVisTypeSelected: (visType: BaseVisType) => void; visTypesRegistry: TypesStart; toggleGroups: (flag: boolean) => void; } diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx index 60857cc39661e..74163296e31fd 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx @@ -8,20 +8,14 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../../vis_types'; +import { TypesStart, BaseVisType, VisGroups } from '../../vis_types'; import { GroupSelection } from './group_selection'; import { DocLinksStart } from '../../../../../core/public'; describe('GroupSelection', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -64,22 +58,22 @@ describe('GroupSelection', () => { aliasPath: '#/anotherUrl', promotion: true, } as unknown, - ] as VisType[]; + ] as BaseVisType[]; - const visTypesRegistry = (visTypes: VisType[]): TypesStart => { + const visTypesRegistry = (visTypes: BaseVisType[]): TypesStart => { return { - get(id: string): VisType { - return (visTypes.find((vis) => vis.name === id) as unknown) as VisType; + get(id: string): BaseVisType { + return (visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, all: () => { - return (visTypes as unknown) as VisType[]; + return (visTypes as unknown) as BaseVisType[]; }, getAliases: () => [], unRegisterAlias: () => [], getByGroup: (group: VisGroups) => { return (visTypes.filter((type) => { return type.group === group; - }) as unknown) as VisType[]; + }) as unknown) as BaseVisType[]; }, }; }; @@ -142,7 +136,7 @@ describe('GroupSelection', () => { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { it('should sort promoted visualizations first', () => { const wrapper = mountWithIntl( { it('should render disabled aliases with a disabled class', () => { const wrapper = mountWithIntl( { it('should render a basic badge with link for disabled aliases with promoTooltip', () => { const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( { }; const wrapper = mountWithIntl( void; + onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; visTypesRegistry: TypesStart; docLinks: DocLinksStart; toggleGroups: (flag: boolean) => void; @@ -43,12 +43,12 @@ interface GroupSelectionProps { } interface VisCardProps { - onVisTypeSelected: (visType: VisType | VisTypeAlias) => void; - visType: VisType | VisTypeAlias; + onVisTypeSelected: (visType: BaseVisType | VisTypeAlias) => void; + visType: BaseVisType | VisTypeAlias; showExperimental?: boolean | undefined; } -function isVisTypeAlias(type: VisType | VisTypeAlias): type is VisTypeAlias { +function isVisTypeAlias(type: BaseVisType | VisTypeAlias): type is VisTypeAlias { return 'aliasPath' in type; } diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx index baaed6d4ea8bf..c8a3c81f517ea 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.test.tsx @@ -8,21 +8,16 @@ import React from 'react'; import { mountWithIntl } from '@kbn/test/jest'; -import { TypesStart, VisType, VisGroups } from '../vis_types'; +import { TypesStart, VisGroups } from '../vis_types'; import NewVisModal from './new_vis_modal'; import { ApplicationStart, SavedObjectsStart, DocLinksStart } from '../../../../core/public'; import { embeddablePluginMock } from '../../../embeddable/public/mocks'; +import { BaseVisType } from '../vis_types'; describe('NewVisModal', () => { const defaultVisTypeParams = { hidden: false, - visualization: class Controller { - public render = jest.fn(); - public destroy = jest.fn(); - }, requiresSearch: false, - requestHandler: 'none', - responseHandler: 'none', }; const _visTypes = [ { @@ -61,21 +56,15 @@ describe('NewVisModal', () => { stage: 'production', ...defaultVisTypeParams, }, - ]; + ] as BaseVisType[]; const visTypes: TypesStart = { - get(id: string): VisType { - return (_visTypes.find((vis) => vis.name === id) as unknown) as VisType; - }, - all: () => { - return (_visTypes as unknown) as VisType[]; + get(id: string): BaseVisType { + return (_visTypes.find((vis) => vis.name === id) as unknown) as BaseVisType; }, + all: () => _visTypes, getAliases: () => [], unRegisterAlias: () => [], - getByGroup: (group: VisGroups) => { - return (_visTypes.filter((type) => { - return type.group === group; - }) as unknown) as VisType[]; - }, + getByGroup: (group: VisGroups) => _visTypes.filter((type) => type.group === group), }; const addBasePath = (url: string) => `testbasepath${url}`; const settingsGet = jest.fn(); diff --git a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx index 2a9641607d17a..b0be73e486d6c 100644 --- a/src/plugins/visualizations/public/wizard/new_vis_modal.tsx +++ b/src/plugins/visualizations/public/wizard/new_vis_modal.tsx @@ -21,7 +21,7 @@ import { import { SearchSelection } from './search_selection'; import { GroupSelection } from './group_selection'; import { AggBasedSelection } from './agg_based_selection'; -import type { TypesStart, VisType, VisTypeAlias } from '../vis_types'; +import type { TypesStart, BaseVisType, VisTypeAlias } from '../vis_types'; import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; import { EmbeddableStateTransfer } from '../../../embeddable/public'; import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants'; @@ -46,7 +46,7 @@ interface TypeSelectionProps { interface TypeSelectionState { showSearchVisModal: boolean; showGroups: boolean; - visType?: VisType; + visType?: BaseVisType; } // TODO: redirect logic is specific to visualise & dashboard @@ -129,7 +129,7 @@ class NewVisModal extends React.Component { + private onVisTypeSelected = (visType: BaseVisType | VisTypeAlias) => { if (!('aliasPath' in visType) && visType.requiresSearch && visType.options.showIndexSelection) { this.setState({ showSearchVisModal: true, @@ -144,7 +144,11 @@ class NewVisModal extends React.Component void; - visType: VisType; + visType: BaseVisType; uiSettings: IUiSettingsClient; savedObjects: SavedObjectsStart; goBack: () => void; diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index dd779800cd452..0e1e5a723373f 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json index 0a47cdb8ff74a..c7b4a0325dc91 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index 992d667fdce9f..fc8622a818dec 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index 031c9f9ea5504..95c011f9259b9 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index 8c6fde201c8f1..f4a8cd1f14e18 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test3.json b/test/interpreter_functional/snapshots/session/combined_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/combined_test3.json +++ b/test/interpreter_functional/snapshots/session/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/final_output_test.json b/test/interpreter_functional/snapshots/session/final_output_test.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/final_output_test.json +++ b/test/interpreter_functional/snapshots/session/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_all_data.json b/test/interpreter_functional/snapshots/session/metric_all_data.json index dd779800cd452..0e1e5a723373f 100644 --- a/test/interpreter_functional/snapshots/session/metric_all_data.json +++ b/test/interpreter_functional/snapshots/session/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_invalid_data.json b/test/interpreter_functional/snapshots/session/metric_invalid_data.json index 0a47cdb8ff74a..c7b4a0325dc91 100644 --- a/test/interpreter_functional/snapshots/session/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/session/metric_invalid_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json index 992d667fdce9f..fc8622a818dec 100644 --- a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json index 031c9f9ea5504..95c011f9259b9 100644 --- a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json index 8c6fde201c8f1..f4a8cd1f14e18 100644 --- a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test3.json b/test/interpreter_functional/snapshots/session/step_output_test3.json index 2aa601a8d3631..64b1052552c8f 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test3.json +++ b/test/interpreter_functional/snapshots/session/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json index 622cbd80090ba..33c8f3238dc47 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/kibana.json @@ -3,6 +3,7 @@ "version": "0.0.1", "kibanaVersion": "kibana", "requiredPlugins": [ + "expressions", "visualizations" ], "server": false, diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts index 7de3965d66d35..d4a7a32ba42dd 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/plugin.ts @@ -7,25 +7,42 @@ */ import { CoreSetup, Plugin } from 'kibana/public'; -import { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; +import { VisualizationsSetup } from 'src/plugins/visualizations/public'; +import { Plugin as ExpressionsPlugin } from '../../../../../src/plugins/expressions/public'; + import { SelfChangingEditor } from './self_changing_vis/self_changing_editor'; -import { SelfChangingComponent } from './self_changing_vis/self_changing_components'; +import { selfChangingVisFn, SelfChangingVisParams } from './self_changing_vis_fn'; +import { selfChangingVisRenderer } from './self_changing_vis_renderer'; +import { toExpressionAst } from './to_ast'; export interface SetupDependencies { + expressions: ReturnType; visualizations: VisualizationsSetup; } export class CustomVisualizationsPublicPlugin implements Plugin { - public setup(core: CoreSetup, setupDeps: SetupDependencies) { - setupDeps.visualizations.createReactVisualization({ + public setup(core: CoreSetup, { expressions, visualizations }: SetupDependencies) { + /** + * Register an expression function with type "render" for your visualization + */ + expressions.registerFunction(selfChangingVisFn); + + /** + * Register a renderer for your visualization + */ + expressions.registerRenderer(selfChangingVisRenderer); + + /** + * Create the visualization type with definition + */ + visualizations.createBaseVisualization({ name: 'self_changing_vis', title: 'Self Changing Vis', icon: 'controlsHorizontal', description: 'This visualization is able to change its own settings, that you could also set in the editor.', visConfig: { - component: SelfChangingComponent, defaults: { counter: 0, }, @@ -39,7 +56,7 @@ export class CustomVisualizationsPublicPlugin }, ], }, - requestHandler: 'none', + toExpressionAst, }); } diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx index 3b3e57bff8aa1..7409bc48d3bc3 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_components.tsx @@ -7,14 +7,13 @@ */ import React, { useEffect } from 'react'; - import { EuiBadge } from '@elastic/eui'; +import { SelfChangingVisParams } from '../self_changing_vis_fn'; + interface SelfChangingComponentProps { - renderComplete: () => {}; - visParams: { - counter: number; - }; + renderComplete(): void; + visParams: SelfChangingVisParams; } export function SelfChangingComponent(props: SelfChangingComponentProps) { diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx index ff3fd63e5d4aa..ad651ab55220d 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx @@ -9,13 +9,13 @@ import React from 'react'; import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; -import { VisOptionsProps } from 'src/plugins/vis_default_editor/public/vis_options_props'; +import { VisEditorOptionsProps } from 'src/plugins/visualizations/public'; interface CounterParams { counter: number; } -export class SelfChangingEditor extends React.Component> { +export class SelfChangingEditor extends React.Component> { onCounterChange = (ev: any) => { this.props.setValue('counter', parseInt(ev.target.value, 10)); }; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts new file mode 100644 index 0000000000000..28bcc27f71ec9 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_fn.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { ExpressionFunctionDefinition, Render } from 'src/plugins/expressions/public'; +import { KibanaContext } from 'src/plugins/data/public'; + +export interface SelfChangingVisParams { + counter: number; +} + +export interface SelfChangingVisRenderValue { + visParams: { + counter: number; + }; +} + +type Output = Promise>; + +export type SelfChangingVisExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'self_changing_vis', + KibanaContext, + SelfChangingVisParams, + Output +>; + +export const selfChangingVisFn: SelfChangingVisExpressionFunctionDefinition = { + name: 'self_changing_vis', + type: 'render', + inputTypes: ['kibana_context'], + help: + 'The expression function definition should be registered for a custom visualization to be rendered', + args: { + counter: { + types: ['number'], + default: 0, + help: 'Visualization only argument with type number', + }, + }, + async fn(input, args) { + /** + * You can do any calculation you need before rendering. + * The function can also do asynchronous operations, e.x.: + * + const calculatedCounter = await new Promise((resolve) => + setTimeout(() => { + resolve(args.counter * 2); + }, 3000) + ); + */ + + return { + type: 'render', + as: 'self_changing_vis', + value: { + visParams: { + counter: args.counter, + }, + }, + }; + }, +}; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx new file mode 100644 index 0000000000000..ea24e6c294880 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis_renderer.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; + +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; +import { SelfChangingComponent } from './self_changing_vis/self_changing_components'; +import { SelfChangingVisRenderValue } from './self_changing_vis_fn'; + +export const selfChangingVisRenderer: ExpressionRenderDefinition = { + name: 'self_changing_vis', + reuseDomNode: true, + render: (domNode, { visParams }, handlers) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render(, domNode); + }, +}; diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts new file mode 100644 index 0000000000000..0f7ecb7954d15 --- /dev/null +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/to_ast.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { VisToExpressionAst } from 'src/plugins/visualizations/public'; +import { + buildExpression, + buildExpressionFunction, +} from '../../../../../src/plugins/expressions/public'; +import { + SelfChangingVisExpressionFunctionDefinition, + SelfChangingVisParams, +} from './self_changing_vis_fn'; + +export const toExpressionAst: VisToExpressionAst = (vis) => { + const { counter } = vis.params; + + const selfChangingVis = buildExpressionFunction( + 'self_changing_vis', + { counter } + ); + + const ast = buildExpression([selfChangingVis]); + + return ast.toAst(); +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ef2149c4931fa..9d8951b03f39a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4693,7 +4693,6 @@ "visualizations.function.visDimension.format.help": "フォーマット", "visualizations.function.visDimension.formatParams.help": "フォーマットパラメーター", "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します", - "visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです", "visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています", "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。", "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 08d064ce8a05c..afa8c6d4be51a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4698,7 +4698,6 @@ "visualizations.function.visDimension.format.help": "格式", "visualizations.function.visDimension.formatParams.help": "格式参数", "visualizations.function.visDimension.help": "生成 visConfig 维度对象", - "visualizations.functions.visualization.help": "简单可视化", "visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合", "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。", "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合", From 1994c5bfd95c3a102e55e667ea75f28a851bc8d0 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 26 Jan 2021 09:49:04 +0100 Subject: [PATCH 45/90] [Core] remove unused "pageNavigation" setting (#89160) * remove unused advanced setting * remove docs --- docs/management/advanced-options.asciidoc | 5 ----- .../ui_settings/settings/navigation.test.ts | 14 ------------- .../server/ui_settings/settings/navigation.ts | 20 ------------------- .../server/collectors/management/schema.ts | 1 - src/plugins/telemetry/schema/oss_plugins.json | 3 --- .../translations/translations/ja-JP.json | 4 ---- .../translations/translations/zh-CN.json | 4 ---- 7 files changed, 51 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index c2306b80734d8..bf4f7d9d82704 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -134,11 +134,6 @@ The maximum numbers of buckets that a single data source can return. This might arise when the user selects a short interval (for example, 1s) for a long time period (1 year). -[[pagenavigation]]`pageNavigation`:: -The style of navigation menu for Kibana. Choices are Legacy, the legacy style -where every plugin is represented in the nav, and Modern, a new format that -bundles related plugins together in flyaway nested navigation. - [[query-allowleadingwildcards]]`query:allowLeadingWildcards`:: Allows a wildcard (*) as the first character in a query clause. Only applies when experimental query features are enabled in the query bar. To disallow diff --git a/src/core/server/ui_settings/settings/navigation.test.ts b/src/core/server/ui_settings/settings/navigation.test.ts index 1ba81b4e79f46..0e6fbdfdbdb6e 100644 --- a/src/core/server/ui_settings/settings/navigation.test.ts +++ b/src/core/server/ui_settings/settings/navigation.test.ts @@ -28,18 +28,4 @@ describe('navigation settings', () => { ); }); }); - - describe('pageNavigation', () => { - const validate = getValidationFn(navigationSettings.pageNavigation); - - it('should only accept valid values', () => { - expect(() => validate('modern')).not.toThrow(); - expect(() => validate('legacy')).not.toThrow(); - expect(() => validate('invalid')).toThrowErrorMatchingInlineSnapshot(` -"types that failed validation: -- [0]: expected value to equal [modern] -- [1]: expected value to equal [legacy]" -`); - }); - }); }); diff --git a/src/core/server/ui_settings/settings/navigation.ts b/src/core/server/ui_settings/settings/navigation.ts index 38064db9e9388..937af4bb9aad1 100644 --- a/src/core/server/ui_settings/settings/navigation.ts +++ b/src/core/server/ui_settings/settings/navigation.ts @@ -37,25 +37,5 @@ export const getNavigationSettings = (): Record => { 'The route must be a relative URL.', }), }, - pageNavigation: { - name: i18n.translate('core.ui_settings.params.pageNavigationName', { - defaultMessage: 'Side nav style', - }), - value: 'modern', - description: i18n.translate('core.ui_settings.params.pageNavigationDesc', { - defaultMessage: 'Change the style of navigation', - }), - type: 'select', - options: ['modern', 'legacy'], - optionLabels: { - modern: i18n.translate('core.ui_settings.params.pageNavigationModern', { - defaultMessage: 'Modern', - }), - legacy: i18n.translate('core.ui_settings.params.pageNavigationLegacy', { - defaultMessage: 'Legacy', - }), - }, - schema: schema.oneOf([schema.literal('modern'), schema.literal('legacy')]), - }, }; }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index d75b2981035f4..28eeb461f7a86 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -77,7 +77,6 @@ export const stackManagementSchema: MakeSchemaFrom = { 'sort:options': { type: 'keyword' }, 'savedObjects:listingLimit': { type: 'long' }, 'query:queryString:options': { type: 'keyword' }, - pageNavigation: { type: 'keyword' }, 'metrics:max_buckets': { type: 'long' }, 'query:allowLeadingWildcards': { type: 'boolean' }, metaFields: { type: 'keyword' }, // it's an array diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 27d9b5ce83203..7bac6a809eca3 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -4268,9 +4268,6 @@ "query:queryString:options": { "type": "keyword" }, - "pageNavigation": { - "type": "keyword" - }, "metrics:max_buckets": { "type": "long" }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9d8951b03f39a..93267c950c10e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -529,10 +529,6 @@ "core.ui_settings.params.notifications.infoLifetimeTitle": "情報通知時間", "core.ui_settings.params.notifications.warningLifetimeText": "警告通知が画面に表示される時間(ミリ秒単位)です。{infinityValue}に設定すると、無効になります。", "core.ui_settings.params.notifications.warningLifetimeTitle": "警告通知時間", - "core.ui_settings.params.pageNavigationDesc": "ナビゲーションのスタイルを変更", - "core.ui_settings.params.pageNavigationLegacy": "レガシー", - "core.ui_settings.params.pageNavigationModern": "モダン", - "core.ui_settings.params.pageNavigationName": "サイドナビゲーションスタイル", "core.ui_settings.params.storeUrlText": "URLが長くなりすぎるためブラウザーが対応できない場合があります。セッションストレージにURLの一部を保存することでこの問題に対処できるかどうかをテストしています。結果を教えてください!", "core.ui_settings.params.storeUrlTitle": "セッションストレージにURLを格納", "core.ui_settings.params.themeVersionText": "現在のバージョンと次のバージョンのKibanaで使用されるテーマを切り替えます。この設定を適用するにはページの更新が必要です。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index afa8c6d4be51a..41bdf97333dd7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -529,10 +529,6 @@ "core.ui_settings.params.notifications.infoLifetimeTitle": "信息通知生存时间", "core.ui_settings.params.notifications.warningLifetimeText": "在屏幕上显示警告通知的时间(毫秒)。设置为 {infinityValue} 将禁用此项。", "core.ui_settings.params.notifications.warningLifetimeTitle": "警告通知生存时间", - "core.ui_settings.params.pageNavigationDesc": "更改导航样式", - "core.ui_settings.params.pageNavigationLegacy": "旧版", - "core.ui_settings.params.pageNavigationModern": "现代", - "core.ui_settings.params.pageNavigationName": "侧边导航样式", "core.ui_settings.params.storeUrlText": "有时,URL 可能会变得过长,使某些浏览器无法进行处理。为此,我们将正测试在会话存储中存储 URL 的组成部分是否会有所帮助。请向我们反馈您的体验!", "core.ui_settings.params.storeUrlTitle": "将 URL 存储在会话存储中", "core.ui_settings.params.themeVersionText": "在用于当前版和下一版 Kibana 的主题之间切换。需要刷新页面,才能应用设置。", From de44af3eebfacbb91af3ddd2d8f79959069cad72 Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Tue, 26 Jan 2021 13:08:03 +0200 Subject: [PATCH 46/90] Improve extend search session UI (#89126) * Fix extend search session UI * code review --- x-pack/plugins/data_enhanced/public/plugin.ts | 4 +-- .../sessions_mgmt/application/index.tsx | 4 +-- .../components/actions/extend_button.tsx | 14 +++++++-- .../components/actions/get_action.tsx | 13 +++++++-- .../sessions_mgmt/components/main.test.tsx | 17 ++++++----- .../search/sessions_mgmt/components/main.tsx | 4 +-- .../components/table/table.test.tsx | 29 ++++++++++++------- .../sessions_mgmt/components/table/table.tsx | 11 +++---- .../public/search/sessions_mgmt/index.ts | 6 ++-- .../search/sessions_mgmt/lib/api.test.ts | 24 +++++++++------ .../public/search/sessions_mgmt/lib/api.ts | 21 +++++++++----- .../sessions_mgmt/lib/get_columns.test.tsx | 17 ++++++----- .../search/sessions_mgmt/lib/get_columns.tsx | 4 +-- .../lib/get_expiration_status.ts | 6 ++-- .../server/search/session/monitoring_task.ts | 2 +- 15 files changed, 110 insertions(+), 66 deletions(-) diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index add7a966fee34..edcdea3997c8e 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -66,8 +66,8 @@ export class DataEnhancedPlugin this.config = this.initializerContext.config.get(); if (this.config.search.sessions.enabled) { - const { management: sessionsMgmtConfig } = this.config.search.sessions; - registerSearchSessionsMgmt(core, sessionsMgmtConfig, { management }); + const sessionsConfig = this.config.search.sessions; + registerSearchSessionsMgmt(core, sessionsConfig, { management }); } } diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx index 27f1482a4d20d..7347f070e91c3 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/application/index.tsx @@ -10,7 +10,7 @@ import type { AppDependencies, IManagementSectionsPluginsSetup, IManagementSectionsPluginsStart, - SessionsMgmtConfigSchema, + SessionsConfigSchema, } from '../'; import { APP } from '../'; import { SearchSessionsMgmtAPI } from '../lib/api'; @@ -20,7 +20,7 @@ import { renderApp } from './render'; export class SearchSessionsMgmtApp { constructor( private coreSetup: CoreSetup, - private config: SessionsMgmtConfigSchema, + private config: SessionsConfigSchema, private params: ManagementAppMountParams, private pluginsSetup: IManagementSectionsPluginsSetup ) {} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx index 4c8a7b0217688..d9c2bdabcbac1 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/extend_button.tsx @@ -8,6 +8,8 @@ import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState } from 'react'; +import { Duration } from 'moment'; +import moment from 'moment'; import { SearchSessionsMgmtAPI } from '../../lib/api'; import { TableText } from '../'; import { OnActionComplete } from './types'; @@ -15,6 +17,8 @@ import { OnActionComplete } from './types'; interface ExtendButtonProps { id: string; name: string; + expires: string | null; + extendBy: Duration; api: SearchSessionsMgmtAPI; onActionComplete: OnActionComplete; } @@ -23,8 +27,11 @@ const ExtendConfirm = ({ onConfirmDismiss, ...props }: ExtendButtonProps & { onConfirmDismiss: () => void }) => { - const { id, name, api, onActionComplete } = props; + const { id, name, expires, api, extendBy, onActionComplete } = props; const [isLoading, setIsLoading] = useState(false); + const extendByDuration = moment.duration(extendBy); + + const newExpiration = moment(expires).add(extendByDuration); const title = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.title', { defaultMessage: 'Extend search session expiration', @@ -36,9 +43,10 @@ const ExtendConfirm = ({ defaultMessage: 'Cancel', }); const message = i18n.translate('xpack.data.mgmt.searchSessions.extendModal.extendMessage', { - defaultMessage: "When would you like the search session '{name}' to expire?", + defaultMessage: "The search session '{name}' expiration would be extended until {newExpires}.", values: { name, + newExpires: newExpiration.toLocaleString(), }, }); @@ -49,7 +57,7 @@ const ExtendConfirm = ({ onCancel={onConfirmDismiss} onConfirm={async () => { setIsLoading(true); - await api.sendExtend(id, '1'); + await api.sendExtend(id, `${extendByDuration.asMilliseconds()}ms`); onActionComplete(); }} confirmButtonText={confirm} diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx index 5bf0fbda5b5cc..c80cf6c244895 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/actions/get_action.tsx @@ -17,7 +17,7 @@ import { ACTION, OnActionComplete } from './types'; export const getAction = ( api: SearchSessionsMgmtAPI, actionType: string, - { id, name, reloadUrl }: UISession, + { id, name, expires, reloadUrl }: UISession, onActionComplete: OnActionComplete ): IClickActionDescriptor | null => { switch (actionType) { @@ -39,7 +39,16 @@ export const getAction = ( return { iconType: extendSessionIcon, textColor: 'default', - label: , + label: ( + + ), }; default: diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx index e01d1a28c5e54..14aea4bcbf59b 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.test.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { act } from 'react-dom/test-utils'; import { coreMock } from 'src/core/public/mocks'; import { SessionsClient } from 'src/plugins/data/public/search'; -import { SessionsMgmtConfigSchema } from '..'; +import { SessionsConfigSchema } from '..'; import { SearchSessionsMgmtAPI } from '../lib/api'; import { AsyncSearchIntroDocumentation } from '../lib/documentation'; import { LocaleWrapper, mockUrls } from '../__mocks__'; @@ -20,7 +20,7 @@ import { SearchSessionsMgmtMain } from './main'; let mockCoreSetup: MockedKeys; let mockCoreStart: MockedKeys; -let mockConfig: SessionsMgmtConfigSchema; +let mockConfig: SessionsConfigSchema; let sessionsClient: SessionsClient; let api: SearchSessionsMgmtAPI; @@ -29,11 +29,14 @@ describe('Background Search Session Management Main', () => { mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); mockConfig = { - expiresSoonWarning: moment.duration(1, 'days'), - maxSessions: 2000, - refreshInterval: moment.duration(1, 'seconds'), - refreshTimeout: moment.duration(10, 'minutes'), - }; + defaultExpiration: moment.duration('7d'), + management: { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }, + } as any; sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx index 80c6a580dd183..cdf92d69f6438 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/main.tsx @@ -17,7 +17,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import type { CoreStart, HttpStart } from 'kibana/public'; import React from 'react'; -import type { SessionsMgmtConfigSchema } from '../'; +import type { SessionsConfigSchema } from '../'; import type { SearchSessionsMgmtAPI } from '../lib/api'; import type { AsyncSearchIntroDocumentation } from '../lib/documentation'; import { TableText } from './'; @@ -29,7 +29,7 @@ interface Props { api: SearchSessionsMgmtAPI; http: HttpStart; timezone: string; - config: SessionsMgmtConfigSchema; + config: SessionsConfigSchema; } export function SearchSessionsMgmtMain({ documentation, ...tableProps }: Props) { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx index 51cec8f2afeff..a99fc26889a24 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.test.tsx @@ -13,14 +13,14 @@ import React from 'react'; import { coreMock } from 'src/core/public/mocks'; import { SessionsClient } from 'src/plugins/data/public/search'; import { SearchSessionStatus } from '../../../../../common/search'; -import { SessionsMgmtConfigSchema } from '../../'; +import { SessionsConfigSchema } from '../../'; import { SearchSessionsMgmtAPI } from '../../lib/api'; import { LocaleWrapper, mockUrls } from '../../__mocks__'; import { SearchSessionsMgmtTable } from './table'; let mockCoreSetup: MockedKeys; let mockCoreStart: CoreStart; -let mockConfig: SessionsMgmtConfigSchema; +let mockConfig: SessionsConfigSchema; let sessionsClient: SessionsClient; let api: SearchSessionsMgmtAPI; @@ -29,11 +29,14 @@ describe('Background Search Session Management Table', () => { mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); mockConfig = { - expiresSoonWarning: moment.duration(1, 'days'), - maxSessions: 2000, - refreshInterval: moment.duration(1, 'seconds'), - refreshTimeout: moment.duration(10, 'minutes'), - }; + defaultExpiration: moment.duration('7d'), + management: { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }, + } as any; sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { @@ -134,7 +137,10 @@ describe('Background Search Session Management Table', () => { sessionsClient.find = jest.fn(); mockConfig = { ...mockConfig, - refreshInterval: moment.duration(10, 'seconds'), + management: { + ...mockConfig.management, + refreshInterval: moment.duration(10, 'seconds'), + }, }; await act(async () => { @@ -162,8 +168,11 @@ describe('Background Search Session Management Table', () => { mockConfig = { ...mockConfig, - refreshInterval: moment.duration(1, 'day'), - refreshTimeout: moment.duration(2, 'days'), + management: { + ...mockConfig.management, + refreshInterval: moment.duration(1, 'day'), + refreshTimeout: moment.duration(2, 'days'), + }, }; await act(async () => { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx index f7aecdbd58a23..290fa4d74dfeb 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/components/table/table.tsx @@ -12,7 +12,7 @@ import React, { useCallback, useMemo, useRef, useEffect, useState } from 'react' import useDebounce from 'react-use/lib/useDebounce'; import useInterval from 'react-use/lib/useInterval'; import { TableText } from '../'; -import { SessionsMgmtConfigSchema } from '../..'; +import { SessionsConfigSchema } from '../..'; import { SearchSessionsMgmtAPI } from '../../lib/api'; import { getColumns } from '../../lib/get_columns'; import { UISession } from '../../types'; @@ -26,7 +26,7 @@ interface Props { core: CoreStart; api: SearchSessionsMgmtAPI; timezone: string; - config: SessionsMgmtConfigSchema; + config: SessionsConfigSchema; } export function SearchSessionsMgmtTable({ core, api, timezone, config, ...props }: Props) { @@ -35,9 +35,10 @@ export function SearchSessionsMgmtTable({ core, api, timezone, config, ...props const [debouncedIsLoading, setDebouncedIsLoading] = useState(false); const [pagination, setPagination] = useState({ pageIndex: 0 }); const showLatestResultsHandler = useRef(); - const refreshInterval = useMemo(() => moment.duration(config.refreshInterval).asMilliseconds(), [ - config.refreshInterval, - ]); + const refreshInterval = useMemo( + () => moment.duration(config.management.refreshInterval).asMilliseconds(), + [config.management.refreshInterval] + ); // Debounce rendering the state of the Refresh button useDebounce( diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts index 76a5d440cd898..695252462794b 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/index.ts @@ -33,7 +33,7 @@ export interface AppDependencies { api: SearchSessionsMgmtAPI; http: HttpStart; i18n: I18nStart; - config: SessionsMgmtConfigSchema; + config: SessionsConfigSchema; } export const APP = { @@ -44,11 +44,11 @@ export const APP = { }), }; -export type SessionsMgmtConfigSchema = ConfigSchema['search']['sessions']['management']; +export type SessionsConfigSchema = ConfigSchema['search']['sessions']; export function registerSearchSessionsMgmt( coreSetup: CoreSetup, - config: SessionsMgmtConfigSchema, + config: SessionsConfigSchema, services: IManagementSectionsPluginsSetup ) { services.management.sections.section.kibana.registerApp({ diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts index 5b337dfd03eb1..068225d0df8c3 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.test.ts @@ -11,14 +11,14 @@ import { coreMock } from 'src/core/public/mocks'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import type { SavedObjectsFindResponse } from 'src/core/server'; import { SessionsClient } from 'src/plugins/data/public/search'; -import type { SessionsMgmtConfigSchema } from '../'; +import type { SessionsConfigSchema } from '../'; import { SearchSessionStatus } from '../../../../common/search'; import { mockUrls } from '../__mocks__'; import { SearchSessionsMgmtAPI } from './api'; let mockCoreSetup: MockedKeys; let mockCoreStart: MockedKeys; -let mockConfig: SessionsMgmtConfigSchema; +let mockConfig: SessionsConfigSchema; let sessionsClient: SessionsClient; describe('Search Sessions Management API', () => { @@ -26,11 +26,14 @@ describe('Search Sessions Management API', () => { mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); mockConfig = { - expiresSoonWarning: moment.duration('1d'), - maxSessions: 2000, - refreshInterval: moment.duration('1s'), - refreshTimeout: moment.duration('10m'), - }; + defaultExpiration: moment.duration('7d'), + management: { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }, + } as any; sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); }); @@ -93,8 +96,11 @@ describe('Search Sessions Management API', () => { test('handle timeout error', async () => { mockConfig = { ...mockConfig, - refreshInterval: moment.duration(1, 'hours'), - refreshTimeout: moment.duration(1, 'seconds'), + management: { + ...mockConfig.management, + refreshInterval: moment.duration(1, 'hours'), + refreshTimeout: moment.duration(1, 'seconds'), + }, }; sessionsClient.find = jest.fn().mockImplementation(async () => { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts index a2bd6b1a549be..c6a3d088b3cda 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/api.ts @@ -10,7 +10,7 @@ import moment from 'moment'; import { from, race, timer } from 'rxjs'; import { mapTo, tap } from 'rxjs/operators'; import type { SharePluginStart } from 'src/plugins/share/public'; -import { SessionsMgmtConfigSchema } from '../'; +import { SessionsConfigSchema } from '../'; import type { ISessionsClient } from '../../../../../../../src/plugins/data/public'; import type { SearchSessionSavedObjectAttributes } from '../../../../common'; import { SearchSessionStatus } from '../../../../common/search'; @@ -47,10 +47,9 @@ async function getUrlFromState( } // Helper: factory for a function to map server objects to UI objects -const mapToUISession = ( - urls: UrlGeneratorsStart, - { expiresSoonWarning }: SessionsMgmtConfigSchema -) => async (savedObject: SavedObject): Promise => { +const mapToUISession = (urls: UrlGeneratorsStart, config: SessionsConfigSchema) => async ( + savedObject: SavedObject +): Promise => { const { name, appId, @@ -92,7 +91,7 @@ interface SearcgSessuibManagementDeps { export class SearchSessionsMgmtAPI { constructor( private sessionsClient: ISessionsClient, - private config: SessionsMgmtConfigSchema, + private config: SessionsConfigSchema, private deps: SearcgSessuibManagementDeps ) {} @@ -101,12 +100,14 @@ export class SearchSessionsMgmtAPI { saved_objects: object[]; } - const refreshTimeout = moment.duration(this.config.refreshTimeout); + const mgmtConfig = this.config.management; + + const refreshTimeout = moment.duration(mgmtConfig.refreshTimeout); const fetch$ = from( this.sessionsClient.find({ page: 1, - perPage: this.config.maxSessions, + perPage: mgmtConfig.maxSessions, sortField: 'created', sortOrder: 'asc', }) @@ -149,6 +150,10 @@ export class SearchSessionsMgmtAPI { this.deps.application.navigateToUrl(reloadUrl); } + public getExtendByDuration() { + return this.config.defaultExpiration; + } + // Cancel and expire public async sendCancel(id: string): Promise { try { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx index ce441efea7385..ec4f2f63ceed1 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.test.tsx @@ -12,7 +12,7 @@ import moment from 'moment'; import { ReactElement } from 'react'; import { coreMock } from 'src/core/public/mocks'; import { SessionsClient } from 'src/plugins/data/public/search'; -import { SessionsMgmtConfigSchema } from '../'; +import { SessionsConfigSchema } from '../'; import { SearchSessionStatus } from '../../../../common/search'; import { OnActionComplete } from '../components'; import { UISession } from '../types'; @@ -22,7 +22,7 @@ import { getColumns } from './get_columns'; let mockCoreSetup: MockedKeys; let mockCoreStart: CoreStart; -let mockConfig: SessionsMgmtConfigSchema; +let mockConfig: SessionsConfigSchema; let api: SearchSessionsMgmtAPI; let sessionsClient: SessionsClient; let handleAction: OnActionComplete; @@ -35,11 +35,14 @@ describe('Search Sessions Management table column factory', () => { mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); mockConfig = { - expiresSoonWarning: moment.duration(1, 'days'), - maxSessions: 2000, - refreshInterval: moment.duration(1, 'seconds'), - refreshTimeout: moment.duration(10, 'minutes'), - }; + defaultExpiration: moment.duration('7d'), + management: { + expiresSoonWarning: moment.duration(1, 'days'), + maxSessions: 2000, + refreshInterval: moment.duration(1, 'seconds'), + refreshTimeout: moment.duration(10, 'minutes'), + }, + } as any; sessionsClient = new SessionsClient({ http: mockCoreSetup.http }); api = new SearchSessionsMgmtAPI(sessionsClient, mockConfig, { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx index 090336c37a98f..1ced354a28039 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_columns.tsx @@ -20,7 +20,7 @@ import { capitalize } from 'lodash'; import React from 'react'; import { FormattedMessage } from 'react-intl'; import { RedirectAppLinks } from '../../../../../../../src/plugins/kibana_react/public'; -import { SessionsMgmtConfigSchema } from '../'; +import { SessionsConfigSchema } from '../'; import { SearchSessionStatus } from '../../../../common/search'; import { TableText } from '../components'; import { OnActionComplete, PopoverActionsMenu } from '../components'; @@ -45,7 +45,7 @@ function isSessionRestorable(status: SearchSessionStatus) { export const getColumns = ( core: CoreStart, api: SearchSessionsMgmtAPI, - config: SessionsMgmtConfigSchema, + config: SessionsConfigSchema, timezone: string, onActionComplete: OnActionComplete ): Array> => { diff --git a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts index 3c167d6dbe41a..5a52fce760d78 100644 --- a/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts +++ b/x-pack/plugins/data_enhanced/public/search/sessions_mgmt/lib/get_expiration_status.ts @@ -6,9 +6,9 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; -import { SessionsMgmtConfigSchema } from '../'; +import { SessionsConfigSchema } from '../'; -export const getExpirationStatus = (config: SessionsMgmtConfigSchema, expires: string | null) => { +export const getExpirationStatus = (config: SessionsConfigSchema, expires: string | null) => { const tNow = moment.utc().valueOf(); const tFuture = moment.utc(expires).valueOf(); @@ -16,7 +16,7 @@ export const getExpirationStatus = (config: SessionsMgmtConfigSchema, expires: s // and the session was early expired when the browser refreshed the listing const durationToExpire = moment.duration(tFuture - tNow); const expiresInDays = Math.floor(durationToExpire.asDays()); - const sufficientDays = Math.ceil(moment.duration(config.expiresSoonWarning).asDays()); + const sufficientDays = Math.ceil(moment.duration(config.management.expiresSoonWarning).asDays()); let toolTipContent = i18n.translate('xpack.data.mgmt.searchSessions.status.expiresSoonInDays', { defaultMessage: 'Expires in {numDays} days', diff --git a/x-pack/plugins/data_enhanced/server/search/session/monitoring_task.ts b/x-pack/plugins/data_enhanced/server/search/session/monitoring_task.ts index d32dcf72a4205..332e69b119bb6 100644 --- a/x-pack/plugins/data_enhanced/server/search/session/monitoring_task.ts +++ b/x-pack/plugins/data_enhanced/server/search/session/monitoring_task.ts @@ -76,7 +76,7 @@ export async function scheduleSearchSessionsTasks( params: {}, }); - logger.debug(`Background search task, scheduled to run`); + logger.debug(`Search sessions task, scheduled to run`); } catch (e) { logger.debug(`Error scheduling task, received ${e.message}`); } From 88f76b5995889d6a0c28c8a34c20c740d5ae0002 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 26 Jan 2021 12:26:11 +0100 Subject: [PATCH 47/90] Reduce index management plugin page load bundle size (#88656) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../helpers/setup_environment.tsx | 2 +- .../__jest__/components/index_table.test.js | 2 +- .../common/constants/index.ts | 1 - .../plugins/index_management/common/index.ts | 2 +- .../application/mount_management_section.ts | 41 +++++++++++++++---- .../store/selectors/extension_service.ts | 19 +++++++++ .../application/store/selectors/index.js | 10 +---- .../store/selectors/indices_filter.test.ts | 3 +- .../plugins/index_management/public/index.ts | 2 +- .../plugins/index_management/public/plugin.ts | 35 ++++++---------- .../plugins/index_management/server/plugin.ts | 2 +- 11 files changed, 72 insertions(+), 47 deletions(-) create mode 100644 x-pack/plugins/index_management/public/application/store/selectors/extension_service.ts diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 87e16b0d7bfe0..afafdb2c6cda6 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -23,7 +23,7 @@ import { notificationService } from '../../../public/application/services/notifi import { ExtensionsService } from '../../../public/services'; import { UiMetricService } from '../../../public/application/services/ui_metric'; import { setUiMetricService } from '../../../public/application/services/api'; -import { setExtensionsService } from '../../../public/application/store/selectors'; +import { setExtensionsService } from '../../../public/application/store/selectors/extension_service'; import { MappingsEditorProvider, ComponentTemplatesProvider, diff --git a/x-pack/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/plugins/index_management/__jest__/components/index_table.test.js index b2526d6b4db5e..2649986c4229e 100644 --- a/x-pack/plugins/index_management/__jest__/components/index_table.test.js +++ b/x-pack/plugins/index_management/__jest__/components/index_table.test.js @@ -27,7 +27,7 @@ import { notificationService } from '../../public/application/services/notificat import { httpService } from '../../public/application/services/http'; import { setUiMetricService } from '../../public/application/services/api'; import { indexManagementStore } from '../../public/application/store'; -import { setExtensionsService } from '../../public/application/store/selectors'; +import { setExtensionsService } from '../../public/application/store/selectors/extension_service'; import { BASE_PATH, API_BASE_PATH } from '../../common/constants'; import { ExtensionsService } from '../../public/services'; import sinon from 'sinon'; diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts index 11240271503e2..5adb9f6098348 100644 --- a/x-pack/plugins/index_management/common/constants/index.ts +++ b/x-pack/plugins/index_management/common/constants/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { PLUGIN } from './plugin'; export { BASE_PATH } from './base_path'; export { API_BASE_PATH } from './api_base_path'; export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters'; diff --git a/x-pack/plugins/index_management/common/index.ts b/x-pack/plugins/index_management/common/index.ts index 119d4e0c54edd..ea5ee5ee8e001 100644 --- a/x-pack/plugins/index_management/common/index.ts +++ b/x-pack/plugins/index_management/common/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { PLUGIN, API_BASE_PATH, BASE_PATH } from './constants'; +export { API_BASE_PATH, BASE_PATH } from './constants'; export { getTemplateParameter } from './lib'; diff --git a/x-pack/plugins/index_management/public/application/mount_management_section.ts b/x-pack/plugins/index_management/public/application/mount_management_section.ts index ff7fc03ef7ae6..b94718c14d3aa 100644 --- a/x-pack/plugins/index_management/public/application/mount_management_section.ts +++ b/x-pack/plugins/index_management/public/application/mount_management_section.ts @@ -10,28 +10,46 @@ import { ManagementAppMountParams } from 'src/plugins/management/public/'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; import { FleetSetup } from '../../../fleet/public'; -import { PLUGIN } from '../../common/constants'; +import { UIM_APP_NAME } from '../../common/constants'; +import { PLUGIN } from '../../common/constants/plugin'; import { ExtensionsService } from '../services'; import { StartDependencies } from '../types'; import { AppDependencies } from './app_context'; import { breadcrumbService } from './services/breadcrumbs'; import { documentationService } from './services/documentation'; -import { HttpService, NotificationService, UiMetricService } from './services'; +import { UiMetricService } from './services'; import { renderApp } from '.'; +import { setUiMetricService } from './services/api'; +import { notificationService } from './services/notification'; +import { httpService } from './services/http'; -interface InternalServices { - httpService: HttpService; - notificationService: NotificationService; - uiMetricService: UiMetricService; - extensionsService: ExtensionsService; +function initSetup({ + usageCollection, + coreSetup, +}: { + coreSetup: CoreSetup; + usageCollection: UsageCollectionSetup; +}) { + const { http, notifications } = coreSetup; + + httpService.setup(http); + notificationService.setup(notifications); + + const uiMetricService = new UiMetricService(UIM_APP_NAME); + + setUiMetricService(uiMetricService); + + uiMetricService.setup(usageCollection); + + return { uiMetricService }; } export async function mountManagementSection( coreSetup: CoreSetup, usageCollection: UsageCollectionSetup, - services: InternalServices, params: ManagementAppMountParams, + extensionsService: ExtensionsService, fleet?: FleetSetup ) { const { element, setBreadcrumbs, history } = params; @@ -50,6 +68,11 @@ export async function mountManagementSection( breadcrumbService.setup(setBreadcrumbs); documentationService.setup(docLinks); + const { uiMetricService } = initSetup({ + usageCollection, + coreSetup, + }); + const appDependencies: AppDependencies = { core: { fatalErrors, @@ -59,7 +82,7 @@ export async function mountManagementSection( usageCollection, fleet, }, - services, + services: { httpService, notificationService, uiMetricService, extensionsService }, history, setBreadcrumbs, uiSettings, diff --git a/x-pack/plugins/index_management/public/application/store/selectors/extension_service.ts b/x-pack/plugins/index_management/public/application/store/selectors/extension_service.ts new file mode 100644 index 0000000000000..c5ce1c359b99b --- /dev/null +++ b/x-pack/plugins/index_management/public/application/store/selectors/extension_service.ts @@ -0,0 +1,19 @@ +/* + * 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 { ExtensionsService } from '../../../services'; + +// Temporary hack to provide the extensionsService instance to this file. +// TODO: Refactor and export all the app selectors through the app dependencies context + +let extensionsService; +export const setExtensionsService = (_extensionsService: ExtensionsService) => { + extensionsService = _extensionsService; +}; + +export { extensionsService }; + +// End hack diff --git a/x-pack/plugins/index_management/public/application/store/selectors/index.js b/x-pack/plugins/index_management/public/application/store/selectors/index.js index c80658581dbee..800cf1473d1a7 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/index.js +++ b/x-pack/plugins/index_management/public/application/store/selectors/index.js @@ -9,15 +9,9 @@ import { createSelector } from 'reselect'; import * as qs from 'query-string'; import { indexStatusLabels } from '../../lib/index_status_labels'; import { sortTable } from '../../services'; +import { extensionsService } from './extension_service'; -// Temporary hack to provide the extensionsService instance to this file. -// TODO: Refactor and export all the app selectors through the app dependencies context - -let extensionsService; -export const setExtensionsService = (_extensionsService) => { - extensionsService = _extensionsService; -}; -// End hack +export { extensionsService }; export const getDetailPanelData = (state) => state.detailPanel.data; export const getDetailPanelError = (state) => state.detailPanel.error; diff --git a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts index f230ddd18e9eb..ca63f305b8c60 100644 --- a/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts +++ b/x-pack/plugins/index_management/public/application/store/selectors/indices_filter.test.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ import { ExtensionsService } from '../../../services'; -import { getFilteredIndices, setExtensionsService } from '.'; +import { getFilteredIndices } from '.'; // @ts-ignore import { defaultTableState } from '../reducers/table_state'; +import { setExtensionsService } from './extension_service'; describe('getFilteredIndices selector', () => { let extensionService: ExtensionsService; diff --git a/x-pack/plugins/index_management/public/index.ts b/x-pack/plugins/index_management/public/index.ts index da6d90f22a384..d40d16ad4f74a 100644 --- a/x-pack/plugins/index_management/public/index.ts +++ b/x-pack/plugins/index_management/public/index.ts @@ -15,4 +15,4 @@ export { IndexManagementPluginSetup } from './types'; export { getIndexListUri } from './application/services/routing'; -export { Index } from '../common'; +export type { Index } from '../common'; diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 9eeeaa9d3c723..35413bd4f9ca1 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -6,54 +6,43 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup } from '../../../../src/core/public'; +import { setExtensionsService } from './application/store/selectors/extension_service'; -import { UIM_APP_NAME, PLUGIN } from '../common/constants'; - -import { httpService } from './application/services/http'; -import { notificationService } from './application/services/notification'; -import { UiMetricService } from './application/services/ui_metric'; - -import { setExtensionsService } from './application/store/selectors'; -import { setUiMetricService } from './application/services/api'; +import { ExtensionsService } from './services'; import { IndexManagementPluginSetup, SetupDependencies, StartDependencies } from './types'; -import { ExtensionsService } from './services'; + +// avoid import from index files in plugin.ts, use specific import paths +import { PLUGIN } from '../common/constants/plugin'; export class IndexMgmtUIPlugin { - private uiMetricService = new UiMetricService(UIM_APP_NAME); private extensionsService = new ExtensionsService(); constructor() { // Temporary hack to provide the service instances in module files in order to avoid a big refactor // For the selectors we should expose them through app dependencies and read them from there on each container component. setExtensionsService(this.extensionsService); - setUiMetricService(this.uiMetricService); } public setup( coreSetup: CoreSetup, plugins: SetupDependencies ): IndexManagementPluginSetup { - const { http, notifications } = coreSetup; const { fleet, usageCollection, management } = plugins; - httpService.setup(http); - notificationService.setup(notifications); - this.uiMetricService.setup(usageCollection); - management.sections.section.data.registerApp({ id: PLUGIN.id, title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), order: 0, mount: async (params) => { const { mountManagementSection } = await import('./application/mount_management_section'); - const services = { - httpService, - notificationService, - uiMetricService: this.uiMetricService, - extensionsService: this.extensionsService, - }; - return mountManagementSection(coreSetup, usageCollection, services, params, fleet); + return mountManagementSection( + coreSetup, + usageCollection, + params, + this.extensionsService, + fleet + ); }, }); diff --git a/x-pack/plugins/index_management/server/plugin.ts b/x-pack/plugins/index_management/server/plugin.ts index 3717e7e94d29f..aaa11bc2d47cc 100644 --- a/x-pack/plugins/index_management/server/plugin.ts +++ b/x-pack/plugins/index_management/server/plugin.ts @@ -13,7 +13,7 @@ import { ILegacyCustomClusterClient, } from 'src/core/server'; -import { PLUGIN } from '../common'; +import { PLUGIN } from '../common/constants/plugin'; import { Dependencies } from './types'; import { ApiRoutes } from './routes'; import { License, IndexDataEnricher } from './services'; From c74a2b25a6a740470d8f0c5d8513296d2d55d9d7 Mon Sep 17 00:00:00 2001 From: Dhruv Bodani Date: Tue, 26 Jan 2021 18:11:24 +0530 Subject: [PATCH 48/90] add a "Refresh" button to the alerts list and alert details views (#87016) * add refresh button to alerts list * add refresh button to Alerts Details Page * fix: add EuiFlexItem tag * add tests for refresh button in alerts_list and alert_details * fix: add test for requestRefresh and loadAlerts Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/alert_details.test.tsx | 36 +++++++++++++++++++ .../components/alert_details.tsx | 14 ++++++++ .../components/alerts_list.test.tsx | 7 ++++ .../alerts_list/components/alerts_list.tsx | 12 +++++++ 4 files changed, 69 insertions(+) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index 13f1aea91c7ac..731152f4a9d63 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -796,6 +796,42 @@ describe('edit button', () => { }); }); +describe('refresh button', () => { + it('should call requestRefresh when clicked', () => { + const alert = mockAlert(); + const alertType: AlertType = { + id: '.noop', + name: 'No Op', + actionGroups: [{ id: 'default', name: 'Default' }], + recoveryActionGroup, + actionVariables: { context: [], state: [], params: [] }, + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + producer: ALERTS_FEATURE_ID, + authorizedConsumers, + enabledInLicense: true, + }; + + const requestRefresh = jest.fn(); + const refreshButton = shallow( + + ) + .find('[data-test-subj="refreshAlertsButton"]') + .first(); + + expect(refreshButton.exists()).toBeTruthy(); + + refreshButton.simulate('click'); + expect(requestRefresh).toHaveBeenCalledTimes(1); + }); +}); + function mockAlert(overloads: Partial = {}): Alert { return { id: uuid.v4(), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 5bb8d47988eed..c2a64bfa3a207 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -163,6 +163,20 @@ export const AlertDetails: React.FunctionComponent = ({ ) : null} + + + + + diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 875268bd93112..8ca3edb1c68df 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -270,6 +270,13 @@ describe('alerts_list component with items', () => { expect(wrapper.find('[data-test-subj="alertStatus-ok"]').length).toBeGreaterThan(0); expect(wrapper.find('[data-test-subj="alertStatus-pending"]').length).toBeGreaterThan(0); expect(wrapper.find('[data-test-subj="alertStatus-unknown"]').length).toBe(0); + expect(wrapper.find('[data-test-subj="refreshAlertsButton"]').exists()).toBeTruthy(); + }); + + it('loads alerts when refresh button is clicked', async () => { + await setup(); + wrapper.find('[data-test-subj="refreshAlertsButton"]').first().simulate('click'); + expect(loadAlerts).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index a1a4cfcab18ef..293d471560503 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -406,6 +406,18 @@ export const AlertsList: React.FunctionComponent = () => { selectedStatuses={alertStatusesFilter} onChange={(ids: string[]) => setAlertStatusesFilter(ids)} />, + + + , ]; const authorizedToModifySelectedAlerts = selectedIds.length From dcba2ab8293546530ea2bb701edc8516c8cfd9fc Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Tue, 26 Jan 2021 14:16:08 +0100 Subject: [PATCH 49/90] Delete dead indices.delete code from v1 migrations (#89059) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../saved_objects/migrations/core/call_cluster.ts | 2 +- .../migrations/core/elastic_index.test.ts | 11 ----------- .../saved_objects/migrations/core/elastic_index.ts | 4 ---- .../migrations/core/migration_es_client.ts | 1 - 4 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/core/server/saved_objects/migrations/core/call_cluster.ts b/src/core/server/saved_objects/migrations/core/call_cluster.ts index ba6027f711b62..2cb1656833728 100644 --- a/src/core/server/saved_objects/migrations/core/call_cluster.ts +++ b/src/core/server/saved_objects/migrations/core/call_cluster.ts @@ -18,7 +18,7 @@ export interface CallCluster { (path: 'bulk', opts: { body: object[] }): Promise; (path: 'count', opts: CountOpts): Promise<{ count: number; _shards: ShardsInfo }>; (path: 'clearScroll', opts: { scrollId: string }): Promise; - (path: 'indices.create' | 'indices.delete', opts: IndexCreationOpts): Promise; + (path: 'indices.create', opts: IndexCreationOpts): Promise; (path: 'indices.exists', opts: IndexOpts): Promise; (path: 'indices.existsAlias', opts: { name: string }): Promise; (path: 'indices.get', opts: IndexOpts & Ignorable): Promise; diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts index 32ecea94826ff..4b35b017a7063 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.test.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.test.ts @@ -107,17 +107,6 @@ describe('ElasticIndex', () => { }); }); - describe('deleteIndex', () => { - test('calls indices.delete', async () => { - await Index.deleteIndex(client, '.lotr'); - - expect(client.indices.delete).toHaveBeenCalledTimes(1); - expect(client.indices.delete).toHaveBeenCalledWith({ - index: '.lotr', - }); - }); - }); - describe('claimAlias', () => { test('handles unaliased indices', async () => { client.indices.getAlias.mockResolvedValue( diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index 9cdec926a56ba..aa7802320dfb7 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -221,10 +221,6 @@ export async function createIndex( }); } -export async function deleteIndex(client: MigrationEsClient, index: string) { - await client.indices.delete({ index }); -} - /** * Converts an index to an alias. The `alias` parameter is the desired alias name which currently * is a concrete index. This function will reindex `alias` into a new index, delete the `alias` diff --git a/src/core/server/saved_objects/migrations/core/migration_es_client.ts b/src/core/server/saved_objects/migrations/core/migration_es_client.ts index 2653e96e1b8d5..c4bb19d8223ff 100644 --- a/src/core/server/saved_objects/migrations/core/migration_es_client.ts +++ b/src/core/server/saved_objects/migrations/core/migration_es_client.ts @@ -20,7 +20,6 @@ const methods = [ 'clearScroll', 'count', 'indices.create', - 'indices.delete', 'indices.deleteTemplate', 'indices.get', 'indices.getAlias', From 3121e47a2d62f99add4d487da734f3f8f831cd31 Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Tue, 26 Jan 2021 08:25:58 -0500 Subject: [PATCH 50/90] [Security Solution] [Detections] Only check privileges if index param is not empty in rule definition (#89147) * only check privileges if index param is not empty * fix jest test i added in previous commit, fixes bug where timestamp field was printed twice in partial failure message * adds two unit tests to check the error messages written from hasTimestampFields util function --- .../signals/__mocks__/es_results.ts | 8 +- .../signals/signal_rule_alert_type.test.ts | 29 ++++++ .../signals/signal_rule_alert_type.ts | 85 +++++++++--------- .../detection_engine/signals/utils.test.ts | 89 +++++++++++++++++++ .../lib/detection_engine/signals/utils.ts | 6 +- 5 files changed, 168 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 6718ff2d1f15f..6ff8b61f4120a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -14,11 +14,7 @@ import { SignalHit, WrappedSignalHit, } from '../types'; -import { - Logger, - SavedObject, - SavedObjectsFindResponse, -} from '../../../../../../../../src/core/server'; +import { SavedObject, SavedObjectsFindResponse } from '../../../../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; import { RuleTypeParams } from '../../types'; import { IRuleStatusSOAttributes } from '../../rules/types'; @@ -615,7 +611,7 @@ export const exampleFindRuleStatusResponse: ( saved_objects: mockStatuses.map((obj) => ({ ...obj, score: 1 })), }); -export const mockLogger: Logger = loggingSystemMock.createLogger(); +export const mockLogger = loggingSystemMock.createLogger(); export const sampleBulkErrorItem = ( { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index f00900255a968..cce781f82e64f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -538,6 +538,35 @@ describe('rules_notification_alert_type', () => { expect(ruleStatusService.success).toHaveBeenCalled(); }); + it('should not call checkPrivileges if ML rule', async () => { + const ruleAlert = getMlResult(); + payload = getPayload(ruleAlert, alertServices) as jest.Mocked; + jobsSummaryMock.mockResolvedValue([ + { + id: 'some_job_id', + jobState: 'started', + datafeedState: 'started', + }, + ]); + (findMlSignals as jest.Mock).mockResolvedValue({ + _shards: { failed: 0 }, + hits: { + hits: [{}], + }, + }); + (bulkCreateMlSignals as jest.Mock).mockResolvedValue({ + success: true, + bulkCreateDuration: 1, + createdItemsCount: 1, + errors: [], + }); + (checkPrivileges as jest.Mock).mockClear(); + + await alert.executor(payload); + expect(checkPrivileges).toHaveBeenCalledTimes(0); + expect(ruleStatusService.success).toHaveBeenCalled(); + }); + it('should call scheduleActions if signalsCount was greater than 0 and rule has actions defined', async () => { const ruleAlert = getMlResult(); ruleAlert.actions = [ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 370f2555b02b8..d08ab66af5683 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -186,49 +186,52 @@ export const signalRulesAlertType = ({ // move this collection of lines into a function in utils // so that we can use it in create rules route, bulk, etc. try { - const hasTimestampOverride = timestampOverride != null && !isEmpty(timestampOverride); - const [privileges, timestampFieldCaps] = await Promise.all([ - pipe( - { services, version, index }, - ({ services: svc, version: ver, index: idx }) => - pipe( - tryCatch(() => getInputIndex(svc, ver, idx), toError), - chain((indices) => tryCatch(() => checkPrivileges(svc, indices), toError)) - ), - toPromise - ), - services.scopedClusterClient.fieldCaps({ - index, - fields: hasTimestampOverride - ? ['@timestamp', timestampOverride as string] - : ['@timestamp'], - allow_no_indices: false, - include_unmapped: true, - }), - ]); + if (!isEmpty(index)) { + const hasTimestampOverride = timestampOverride != null && !isEmpty(timestampOverride); + const [privileges, timestampFieldCaps] = await Promise.all([ + pipe( + { services, version, index }, + ({ services: svc, version: ver, index: idx }) => + pipe( + tryCatch(() => getInputIndex(svc, ver, idx), toError), + chain((indices) => tryCatch(() => checkPrivileges(svc, indices), toError)) + ), + toPromise + ), + services.scopedClusterClient.fieldCaps({ + index, + fields: hasTimestampOverride + ? ['@timestamp', timestampOverride as string] + : ['@timestamp'], + allow_no_indices: false, + include_unmapped: true, + }), + ]); - wrotePartialFailureStatus = await flow( - () => - tryCatch( - () => hasReadIndexPrivileges(privileges, logger, buildRuleMessage, ruleStatusService), - toError + wrotePartialFailureStatus = await flow( + () => + tryCatch( + () => + hasReadIndexPrivileges(privileges, logger, buildRuleMessage, ruleStatusService), + toError + ), + chain((wroteStatus) => + tryCatch( + () => + hasTimestampFields( + wroteStatus, + hasTimestampOverride ? (timestampOverride as string) : '@timestamp', + timestampFieldCaps, + ruleStatusService, + logger, + buildRuleMessage + ), + toError + ) ), - chain((wroteStatus) => - tryCatch( - () => - hasTimestampFields( - wroteStatus, - hasTimestampOverride ? (timestampOverride as string) : '@timestamp', - timestampFieldCaps, - ruleStatusService, - logger, - buildRuleMessage - ), - toError - ) - ), - toPromise - )(); + toPromise + )(); + } } catch (exc) { logger.error(buildRuleMessage(`Check privileges failed to execute ${exc}`)); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index ac004a7fff1fa..ec55ad7f588e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -6,6 +6,7 @@ import moment from 'moment'; import sinon from 'sinon'; +import { ApiResponse, Context } from '@elastic/elasticsearch/lib/Transport'; import { alertsMock, AlertServicesMock } from '../../../../../alerts/server/mocks'; import { listMock } from '../../../../../lists/server/mocks'; @@ -28,6 +29,7 @@ import { getListsClient, getSignalTimeTuples, getExceptions, + hasTimestampFields, wrapBuildingBlocks, generateSignalId, createErrorsFromShard, @@ -61,6 +63,14 @@ const buildRuleMessage = buildRuleMessageFactory({ name: 'fake name', }); +const ruleStatusServiceMock = { + success: jest.fn(), + find: jest.fn(), + goingToRun: jest.fn(), + error: jest.fn(), + partialFailure: jest.fn(), +}; + describe('utils', () => { const anchor = '2020-01-01T06:06:06.666Z'; const unix = moment(anchor).valueOf(); @@ -803,6 +813,85 @@ describe('utils', () => { }); }); + describe('hasTimestampFields', () => { + test('returns true when missing timestamp override field', async () => { + const timestampField = 'event.ingested'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const timestampFieldCapsResponse: Partial, Context>> = { + body: { + fields: { + [timestampField]: { + date: { + type: 'date', + searchable: true, + aggregatable: true, + indices: ['myfakeindex-3', 'myfakeindex-4'], + }, + unmapped: { + type: 'unmapped', + searchable: false, + aggregatable: false, + indices: ['myfakeindex-1', 'myfakeindex-2'], + }, + }, + }, + }, + }; + mockLogger.error.mockClear(); + const res = await hasTimestampFields( + false, + timestampField, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + timestampFieldCapsResponse as ApiResponse>, + ruleStatusServiceMock, + mockLogger, + buildRuleMessage + ); + expect(mockLogger.error).toHaveBeenCalledWith( + 'The following indices are missing the timestamp override field "event.ingested": ["myfakeindex-1","myfakeindex-2"] name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' + ); + expect(res).toBeTruthy(); + }); + test('returns true when missing timestamp field', async () => { + const timestampField = '@timestamp'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const timestampFieldCapsResponse: Partial, Context>> = { + body: { + fields: { + [timestampField]: { + date: { + type: 'date', + searchable: true, + aggregatable: true, + indices: ['myfakeindex-3', 'myfakeindex-4'], + }, + unmapped: { + type: 'unmapped', + searchable: false, + aggregatable: false, + indices: ['myfakeindex-1', 'myfakeindex-2'], + }, + }, + }, + }, + }; + mockLogger.error.mockClear(); + const res = await hasTimestampFields( + false, + timestampField, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + timestampFieldCapsResponse as ApiResponse>, + ruleStatusServiceMock, + mockLogger, + buildRuleMessage + ); + expect(mockLogger.error).toHaveBeenCalledWith( + 'The following indices are missing the timestamp field "@timestamp": ["myfakeindex-1","myfakeindex-2"] name: "fake name" id: "fake id" rule id: "fake rule id" signals index: "fakeindex"' + ); + expect(res).toBeTruthy(); + }); + }); + describe('wrapBuildingBlocks', () => { it('should generate a unique id for each building block', () => { const wrappedBlocks = wrapBuildingBlocks( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index 23c5e6499c8ad..0ad502b67fbe6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -120,8 +120,10 @@ export const hasTimestampFields = async ( // if there is a timestamp override and the unmapped array for the timestamp override key is not empty, // partial failure const errorString = `The following indices are missing the ${ - timestampField === '@timestamp' ? 'timestamp field "@timestamp"' : 'timestamp override field' - } "${timestampField}": ${JSON.stringify( + timestampField === '@timestamp' + ? 'timestamp field "@timestamp"' + : `timestamp override field "${timestampField}"` + }: ${JSON.stringify( isEmpty(timestampFieldCapsResponse.body.fields) ? timestampFieldCapsResponse.body.indices : timestampFieldCapsResponse.body.fields[timestampField].unmapped.indices From ba37975fbc751e310bc921309acb97547fcd1f54 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 26 Jan 2021 08:41:23 -0600 Subject: [PATCH 51/90] Show legend on error rate chart (#89234) This probably was hidden for reasons related to implementing (or deferring the implementation of) comparisons. --- .../shared/charts/transaction_error_rate_chart/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index 90877a895b05b..d712fa27c75ac 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -77,7 +77,6 @@ export function TransactionErrorRateChart({ data: errorRates, type: 'linemark', color: theme.eui.euiColorVis7, - hideLegend: true, title: i18n.translate('xpack.apm.errorRate.chart.errorRate', { defaultMessage: 'Error rate (avg.)', }), From cb9afddb9163283682b797d5920f28c6c6dfb0a2 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 26 Jan 2021 08:35:23 -0700 Subject: [PATCH 52/90] [Maps] add video to maps docs (#89039) * [Maps] add video to maps docs * review feedback, revert some changes * move video to be below intro, clean up geojson upload section * Update docs/maps/index.asciidoc Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> --- docs/maps/index.asciidoc | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/docs/maps/index.asciidoc b/docs/maps/index.asciidoc index 59b592ba1ec59..8f55697249fb2 100644 --- a/docs/maps/index.asciidoc +++ b/docs/maps/index.asciidoc @@ -9,28 +9,40 @@ [partintro] -- -Maps enables you to parse through your geographical data at scale, with speed, and in real time. With features like multiple layers and indices in a map, plotting of raw documents, dynamic client-side styling, and global search across multiple layers, you can understand and monitor your data with ease. +Create beautiful maps from your geographical data. With **Maps**, you can: -With Maps, you can: - -* Create maps with multiple layers and indices. -* Upload GeoJSON files into Elasticsearch. +* Build maps with multiple layers and indices. +* Upload GeoJSON. * Embed your map in dashboards. * Symbolize features using data values. -* Focus in on just the data you want. - -*Ready to get started?* Start your tour of Maps with the <>. +* Focus on only the data that’s important to you. + +*Ready to get started?* Watch the https://videos.elastic.co/watch/BYzRDtH4u7RSD8wKhuEW1b[video], and then start your tour of **Maps** with the <>. + +++++ + + +
+++++ [float] -=== Create maps with multiple layers and indices -You can use multiple layers and indices to show all your data in a single map. This enables your map to show how data sits relative to physical features like weather patterns, human-made features like international borders, and business-specific features like sales regions. You can plot individual documents or use aggregations to plot any data set, no matter how large. +=== Build maps with multiple layers and indices +Use multiple layers and indices to show all your data in a single map. Show how data sits relative to physical features like weather patterns, human-made features like international borders, and business-specific features like sales regions. Plot individual documents or use aggregations to plot any data set, no matter how large. [role="screenshot"] image::maps/images/sample_data_ecommerce.png[] [float] -=== Upload GeoJSON files into Elasticsearch -Maps makes it easy to import geospatial data into the Elastic Stack. Using the GeoJSON Upload feature, you can drag and drop your point and shape data files directly into Elasticsearch, and then use them as layers in the map. +=== Upload GeoJSON +Use **Maps** to drag and drop your GeoJSON points, lines, and polygons into Elasticsearch, and then use them as layers in your map. [float] === Embed your map in dashboards @@ -43,11 +55,11 @@ image::maps/images/embed_in_dashboard.jpeg[] [float] === Symbolize features using data values -You can customize each layer to highlight meaningful dimensions in your data. For example, you can use dark colors to symbolize areas with more web log traffic, and lighter colors to symbolize areas with less traffic. +Customize each layer to highlight meaningful dimensions in your data. For example, use dark colors to symbolize areas with more web log traffic, and lighter colors to symbolize areas with less traffic. [float] -=== Focus in on just the data you want -You can search across your Elasticsearch layers to focus in on just the data you want. Draw a polygon on the map or use the shape from features to create spatial filters to narrow search results to documents that either intersect with, are within, or do not intersect with the specified geometry. Filter individual layers to compares facets. +=== Focus on only the data that’s important to you +Search across your Elasticsearch layers to focus in on just the data you want. Combine free text search with field-based search using the <>. Set the time filter to restrict layers by time. Draw a polygon on the map or use the shape from features to create spatial filters. Filter individual layers to compares facets. -- From a4c884b92bdc081242e3f520f051222f35b9b6b9 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 26 Jan 2021 16:48:47 +0100 Subject: [PATCH 53/90] tsconfig file for lens (#89135) --- x-pack/plugins/lens/kibana.json | 1 - .../suffix_formatter.ts | 8 +++- x-pack/plugins/lens/tsconfig.json | 41 +++++++++++++++++++ x-pack/test/tsconfig.json | 1 + x-pack/tsconfig.json | 2 + x-pack/tsconfig.refs.json | 1 + 6 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 x-pack/plugins/lens/tsconfig.json diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index 4ecc7f0128591..dc0a92ac702d0 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -12,7 +12,6 @@ "urlForwarding", "visualizations", "dashboard", - "charts", "uiActions", "embeddable", "share" diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts index 3d9f3be01a11b..26541c9f890b9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/suffix_formatter.ts @@ -5,7 +5,11 @@ */ import { i18n } from '@kbn/i18n'; -import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../src/plugins/data/public'; +import { + FieldFormat, + FieldFormatInstanceType, + KBN_FIELD_TYPES, +} from '../../../../../src/plugins/data/public'; import { FormatFactory } from '../types'; import { TimeScaleUnit } from './time_scale'; @@ -23,7 +27,7 @@ export const unitSuffixesLong: Record = { d: i18n.translate('xpack.lens.fieldFormats.longSuffix.d', { defaultMessage: 'per day' }), }; -export function getSuffixFormatter(formatFactory: FormatFactory) { +export function getSuffixFormatter(formatFactory: FormatFactory): FieldFormatInstanceType { return class SuffixFormatter extends FieldFormat { static id = 'suffix'; static title = i18n.translate('xpack.lens.fieldFormats.suffix.title', { diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json new file mode 100644 index 0000000000000..7ac5a2980d0ba --- /dev/null +++ b/x-pack/plugins/lens/tsconfig.json @@ -0,0 +1,41 @@ + +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": [ + "*.ts", + "common/**/*", + "public/**/*", + "server/**/*", + "../../typings/**/*" + ], + "references": [ + { "path": "../../../src/core/tsconfig.json" }, + { "path": "../task_manager/tsconfig.json" }, + { "path": "../global_search/tsconfig.json"}, + { "path": "../saved_objects_tagging/tsconfig.json"}, + { "path": "../../../src/plugins/data/tsconfig.json"}, + { "path": "../../../src/plugins/charts/tsconfig.json"}, + { "path": "../../../src/plugins/expressions/tsconfig.json"}, + { "path": "../../../src/plugins/navigation/tsconfig.json" }, + { "path": "../../../src/plugins/url_forwarding/tsconfig.json" }, + { "path": "../../../src/plugins/visualizations/tsconfig.json" }, + { "path": "../../../src/plugins/dashboard/tsconfig.json" }, + { "path": "../../../src/plugins/ui_actions/tsconfig.json" }, + { "path": "../../../src/plugins/embeddable/tsconfig.json" }, + { "path": "../../../src/plugins/share/tsconfig.json" }, + { "path": "../../../src/plugins/usage_collection/tsconfig.json" }, + { "path": "../../../src/plugins/saved_objects/tsconfig.json"}, + { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, + { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, + { "path": "../../../src/plugins/embeddable/tsconfig.json"}, + { "path": "../../../src/plugins/lens_oss/tsconfig.json"}, + { "path": "../../../src/plugins/presentation_util/tsconfig.json"}, + ] + } \ No newline at end of file diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 699ff64af3f88..6368751fedf75 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -46,6 +46,7 @@ { "path": "../plugins/embeddable_enhanced/tsconfig.json" }, { "path": "../plugins/event_log/tsconfig.json" }, { "path": "../plugins/licensing/tsconfig.json" }, + { "path": "../plugins/lens/tsconfig.json" }, { "path": "../plugins/task_manager/tsconfig.json" }, { "path": "../plugins/telemetry_collection_xpack/tsconfig.json" }, { "path": "../plugins/triggers_actions_ui/tsconfig.json" }, diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index ae12773023663..a6eb098b5d678 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -18,6 +18,7 @@ "plugins/embeddable_enhanced/**/*", "plugins/event_log/**/*", "plugins/licensing/**/*", + "plugins/lens/**/*", "plugins/searchprofiler/**/*", "plugins/security_solution/cypress/**/*", "plugins/task_manager/**/*", @@ -84,6 +85,7 @@ { "path": "./plugins/embeddable_enhanced/tsconfig.json" }, { "path": "./plugins/event_log/tsconfig.json" }, { "path": "./plugins/licensing/tsconfig.json" }, + { "path": "./plugins/lens/tsconfig.json" }, { "path": "./plugins/searchprofiler/tsconfig.json" }, { "path": "./plugins/task_manager/tsconfig.json" }, { "path": "./plugins/telemetry_collection_xpack/tsconfig.json" }, diff --git a/x-pack/tsconfig.refs.json b/x-pack/tsconfig.refs.json index 02623b11ce314..6a9e54e2e7adf 100644 --- a/x-pack/tsconfig.refs.json +++ b/x-pack/tsconfig.refs.json @@ -5,6 +5,7 @@ { "path": "./plugins/alerts/tsconfig.json"}, { "path": "./plugins/dashboard_enhanced/tsconfig.json" }, { "path": "./plugins/licensing/tsconfig.json" }, + { "path": "./plugins/lens/tsconfig.json" }, { "path": "./plugins/console_extensions/tsconfig.json" }, { "path": "./plugins/discover_enhanced/tsconfig.json" }, { "path": "./plugins/data_enhanced/tsconfig.json" }, From 110e880fbf653cb6388eaf4c750874a9c24b48ed Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 26 Jan 2021 10:06:38 -0600 Subject: [PATCH 54/90] [Workplace Search] Add source logic and sources logic unit tests (#89247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move shared data to mock * Change name of mock Everywhere else we don’t use the “mock” prefix for mocked data so I’m changing here to match. Also added missing “size” prop from mock. * Remove unused actions These were missed on the migration to the new add_source_logic file. All of that logic lives there now * Add tests for source logic * REmove resetFlashMessages This is no longer used as Kibana resets its own. This was removed from the component already. * Export items for use in tests * Remove unnecessary condition It’s literally not possible for this function to receive an empty contentSources parameter. Not sure why this was added. Even if the server sends response with no privateContentSources, the reducer falls back to an empty array. * Add tests for sources logic * Fix typo --- .../__mocks__/content_sources.mock.ts | 11 + .../workplace_search/__mocks__/meta.mock.ts | 3 +- .../components/source_content.test.tsx | 17 +- .../content_sources/source_logic.test.ts | 444 ++++++++++++++++++ .../views/content_sources/source_logic.ts | 7 - .../content_sources/sources_logic.test.ts | 319 +++++++++++++ .../views/content_sources/sources_logic.ts | 16 +- .../views/groups/groups.test.tsx | 4 +- 8 files changed, 785 insertions(+), 36 deletions(-) create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts create mode 100644 x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts index 0e0d1fa864033..efae95f83034e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/content_sources.mock.ts @@ -323,3 +323,14 @@ export const mostRecentIndexJob = { activeReindexJobId: '123', numDocumentsWithErrors: 1, }; + +export const contentItems = [ + { + id: '1234', + last_updated: '2021-01-21', + }, + { + id: '1235', + last_updated: '2021-01-20', + }, +]; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts index e596ea5d7e948..acfbad1400c66 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/__mocks__/meta.mock.ts @@ -6,10 +6,11 @@ import { DEFAULT_META } from '../../shared/constants'; -export const mockMeta = { +export const meta = { ...DEFAULT_META, page: { current: 1, + size: 5, total_results: 50, total_pages: 5, }, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx index c445a7aec04f6..a404ae508c130 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/components/source_content.test.tsx @@ -20,8 +20,8 @@ import { EuiLink, } from '@elastic/eui'; -import { mockMeta } from '../../../__mocks__/meta.mock'; -import { fullContentSources } from '../../../__mocks__/content_sources.mock'; +import { meta } from '../../../__mocks__/meta.mock'; +import { fullContentSources, contentItems } from '../../../__mocks__/content_sources.mock'; import { DEFAULT_META } from '../../../../shared/constants'; import { ComponentLoader } from '../../../components/shared/component_loader'; @@ -38,17 +38,8 @@ describe('SourceContent', () => { const mockValues = { contentSource: fullContentSources[0], - contentMeta: mockMeta, - contentItems: [ - { - id: '1234', - last_updated: '2021-01-21', - }, - { - id: '1235', - last_updated: '2021-01-20', - }, - ], + contentMeta: meta, + contentItems, contentFilterValue: '', dataLoading: false, sectionLoading: false, diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts new file mode 100644 index 0000000000000..a0efbfe4aca1d --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.test.ts @@ -0,0 +1,444 @@ +/* + * 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 { + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + mockKibanaValues, + expectedAsyncError, +} from '../../../__mocks__'; + +import { AppLogic } from '../../app_logic'; +jest.mock('../../app_logic', () => ({ + AppLogic: { values: { isOrganization: true } }, +})); + +import { + fullContentSources, + sourceConfigData, + contentItems, +} from '../../__mocks__/content_sources.mock'; +import { meta } from '../../__mocks__/meta.mock'; + +import { DEFAULT_META } from '../../../shared/constants'; +import { NOT_FOUND_PATH } from '../../routes'; + +import { SourceLogic } from './source_logic'; + +describe('SourceLogic', () => { + const { http } = mockHttpValues; + const { + clearFlashMessages, + flashAPIErrors, + setSuccessMessage, + setQueuedSuccessMessage, + } = mockFlashMessageHelpers; + const { navigateToUrl } = mockKibanaValues; + const { mount, getListeners } = new LogicMounter(SourceLogic); + + const contentSource = fullContentSources[0]; + + const defaultValues = { + contentSource: {}, + contentItems: [], + sourceConfigData: {}, + dataLoading: true, + sectionLoading: true, + buttonLoading: false, + contentMeta: DEFAULT_META, + contentFilterValue: '', + }; + + const searchServerResponse = { + results: contentItems, + meta, + }; + + beforeEach(() => { + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(SourceLogic.values).toEqual(defaultValues); + }); + + describe('actions', () => { + it('onInitializeSource', () => { + SourceLogic.actions.onInitializeSource(contentSource); + + expect(SourceLogic.values.contentSource).toEqual(contentSource); + expect(SourceLogic.values.dataLoading).toEqual(false); + }); + + it('onUpdateSourceName', () => { + const NAME = 'foo'; + SourceLogic.actions.onInitializeSource(contentSource); + SourceLogic.actions.onUpdateSourceName(NAME); + + expect(SourceLogic.values.contentSource).toEqual({ + ...contentSource, + name: NAME, + }); + expect(setSuccessMessage).toHaveBeenCalled(); + }); + + it('setSourceConfigData', () => { + SourceLogic.actions.setSourceConfigData(sourceConfigData); + + expect(SourceLogic.values.sourceConfigData).toEqual(sourceConfigData); + expect(SourceLogic.values.dataLoading).toEqual(false); + }); + + it('setSearchResults', () => { + SourceLogic.actions.setSearchResults(searchServerResponse); + + expect(SourceLogic.values.contentItems).toEqual(contentItems); + expect(SourceLogic.values.contentMeta).toEqual(meta); + expect(SourceLogic.values.sectionLoading).toEqual(false); + }); + + it('setContentFilterValue', () => { + const VALUE = 'bar'; + SourceLogic.actions.setSearchResults(searchServerResponse); + SourceLogic.actions.onInitializeSource(contentSource); + SourceLogic.actions.setContentFilterValue(VALUE); + + expect(SourceLogic.values.contentMeta).toEqual({ + ...meta, + page: { + ...meta.page, + current: DEFAULT_META.page.current, + }, + }); + expect(SourceLogic.values.contentFilterValue).toEqual(VALUE); + }); + + it('setActivePage', () => { + const PAGE = 2; + SourceLogic.actions.setSearchResults(searchServerResponse); + SourceLogic.actions.setActivePage(PAGE); + + expect(SourceLogic.values.contentMeta).toEqual({ + ...meta, + page: { + ...meta.page, + current: PAGE, + }, + }); + }); + + it('setButtonNotLoading', () => { + // Set button state to loading + SourceLogic.actions.removeContentSource(contentSource.id); + SourceLogic.actions.setButtonNotLoading(); + + expect(SourceLogic.values.buttonLoading).toEqual(false); + }); + }); + + describe('listeners', () => { + describe('initializeSource', () => { + it('calls API and sets values (org)', async () => { + const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource'); + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeSource(contentSource.id); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/123'); + await promise; + expect(onInitializeSourceSpy).toHaveBeenCalledWith(contentSource); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onInitializeSourceSpy = jest.spyOn(SourceLogic.actions, 'onInitializeSource'); + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeSource(contentSource.id); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/123'); + await promise; + expect(onInitializeSourceSpy).toHaveBeenCalledWith(contentSource); + }); + + it('handles federated source', async () => { + AppLogic.values.isOrganization = false; + + const initializeFederatedSummarySpy = jest.spyOn( + SourceLogic.actions, + 'initializeFederatedSummary' + ); + const promise = Promise.resolve({ + ...contentSource, + isFederatedSource: true, + }); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeSource(contentSource.id); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/123'); + await promise; + expect(initializeFederatedSummarySpy).toHaveBeenCalledWith(contentSource.id); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeSource(contentSource.id); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + + it('handles not found state', async () => { + const error = { + response: { + error: 'this is an error', + status: 404, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeSource(contentSource.id); + await expectedAsyncError(promise); + + expect(navigateToUrl).toHaveBeenCalledWith(NOT_FOUND_PATH); + }); + }); + + describe('initializeFederatedSummary', () => { + it('calls API and sets values', async () => { + const onUpdateSummarySpy = jest.spyOn(SourceLogic.actions, 'onUpdateSummary'); + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeFederatedSummary(contentSource.id); + + expect(http.get).toHaveBeenCalledWith( + '/api/workplace_search/org/sources/123/federated_summary' + ); + await promise; + expect(onUpdateSummarySpy).toHaveBeenCalledWith(contentSource.summary); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + SourceLogic.actions.initializeFederatedSummary(contentSource.id); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('searchContentSourceDocuments', () => { + const mockBreakpoint = jest.fn(); + const values = { contentMeta: meta, contentFilterValue: '' }; + const actions = { setSearchResults: jest.fn() }; + const { searchContentSourceDocuments } = getListeners({ + values, + actions, + }); + + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const promise = Promise.resolve(searchServerResponse); + http.post.mockReturnValue(promise); + + await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint); + expect(http.post).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/documents', { + body: JSON.stringify({ query: '', page: meta.page }), + }); + + await promise; + expect(actions.setSearchResults).toHaveBeenCalledWith(searchServerResponse); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + const promise = Promise.resolve(searchServerResponse); + http.post.mockReturnValue(promise); + + SourceLogic.actions.searchContentSourceDocuments(contentSource.id); + await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint); + expect(http.post).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/123/documents', + { + body: JSON.stringify({ query: '', page: meta.page }), + } + ); + + await promise; + expect(actions.setSearchResults).toHaveBeenCalledWith(searchServerResponse); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.post.mockReturnValue(promise); + + await searchContentSourceDocuments({ sourceId: contentSource.id }, mockBreakpoint); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('updateContentSource', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + + const onUpdateSourceNameSpy = jest.spyOn(SourceLogic.actions, 'onUpdateSourceName'); + const promise = Promise.resolve(contentSource); + http.patch.mockReturnValue(promise); + SourceLogic.actions.updateContentSource(contentSource.id, contentSource); + + expect(http.patch).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/settings', { + body: JSON.stringify({ content_source: contentSource }), + }); + await promise; + expect(onUpdateSourceNameSpy).toHaveBeenCalledWith(contentSource.name); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const onUpdateSourceNameSpy = jest.spyOn(SourceLogic.actions, 'onUpdateSourceName'); + const promise = Promise.resolve(contentSource); + http.patch.mockReturnValue(promise); + SourceLogic.actions.updateContentSource(contentSource.id, contentSource); + + expect(http.patch).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/123/settings', + { + body: JSON.stringify({ content_source: contentSource }), + } + ); + await promise; + expect(onUpdateSourceNameSpy).toHaveBeenCalledWith(contentSource.name); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.patch.mockReturnValue(promise); + SourceLogic.actions.updateContentSource(contentSource.id, contentSource); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('removeContentSource', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + + const setButtonNotLoadingSpy = jest.spyOn(SourceLogic.actions, 'setButtonNotLoading'); + const promise = Promise.resolve(contentSource); + http.delete.mockReturnValue(promise); + SourceLogic.actions.removeContentSource(contentSource.id); + + expect(clearFlashMessages).toHaveBeenCalled(); + expect(http.delete).toHaveBeenCalledWith('/api/workplace_search/org/sources/123'); + await promise; + expect(setQueuedSuccessMessage).toHaveBeenCalled(); + expect(setButtonNotLoadingSpy).toHaveBeenCalled(); + }); + + it('calls API and sets values (account)', async () => { + AppLogic.values.isOrganization = false; + + const setButtonNotLoadingSpy = jest.spyOn(SourceLogic.actions, 'setButtonNotLoading'); + const promise = Promise.resolve(contentSource); + http.delete.mockReturnValue(promise); + SourceLogic.actions.removeContentSource(contentSource.id); + + expect(clearFlashMessages).toHaveBeenCalled(); + expect(http.delete).toHaveBeenCalledWith('/api/workplace_search/account/sources/123'); + await promise; + expect(setButtonNotLoadingSpy).toHaveBeenCalled(); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.delete.mockReturnValue(promise); + SourceLogic.actions.removeContentSource(contentSource.id); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('getSourceConfigData', () => { + const serviceType = 'github'; + + it('calls API and sets values', async () => { + AppLogic.values.isOrganization = true; + + const setSourceConfigDataSpy = jest.spyOn(SourceLogic.actions, 'setSourceConfigData'); + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + SourceLogic.actions.getSourceConfigData(serviceType); + + expect(http.get).toHaveBeenCalledWith( + `/api/workplace_search/org/settings/connectors/${serviceType}` + ); + await promise; + expect(setSourceConfigDataSpy).toHaveBeenCalled(); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + SourceLogic.actions.getSourceConfigData(serviceType); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + it('resetSourceState', () => { + SourceLogic.actions.resetSourceState(); + + expect(clearFlashMessages).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts index 2de70009c56a2..ba5c29c190f95 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/source_logic.ts @@ -126,29 +126,22 @@ export const SourceLogic = kea>({ onInitializeSource: () => false, setSourceConfigData: () => false, resetSourceState: () => false, - setPreContentSourceConfigData: () => false, }, ], buttonLoading: [ false, { setButtonNotLoading: () => false, - setSourceConnectData: () => false, setSourceConfigData: () => false, resetSourceState: () => false, removeContentSource: () => true, - saveSourceConfig: () => true, - getSourceConnectData: () => true, - createContentSource: () => true, }, ], sectionLoading: [ true, { searchContentSourceDocuments: () => true, - getPreContentSourceConfigData: () => true, setSearchResults: () => false, - setPreContentSourceConfigData: () => false, }, ], contentItems: [ diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts new file mode 100644 index 0000000000000..11e3a52081637 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.test.ts @@ -0,0 +1,319 @@ +/* + * 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 { + LogicMounter, + mockFlashMessageHelpers, + mockHttpValues, + expectedAsyncError, +} from '../../../__mocks__'; + +import { AppLogic } from '../../app_logic'; +jest.mock('../../app_logic', () => ({ + AppLogic: { values: { isOrganization: true } }, +})); + +import { configuredSources, contentSources } from '../../__mocks__/content_sources.mock'; + +import { SourcesLogic, fetchSourceStatuses, POLLING_INTERVAL } from './sources_logic'; + +describe('SourcesLogic', () => { + const { http } = mockHttpValues; + const { flashAPIErrors, setQueuedSuccessMessage } = mockFlashMessageHelpers; + const { mount, unmount } = new LogicMounter(SourcesLogic); + + const contentSource = contentSources[0]; + + const defaultValues = { + contentSources: [], + privateContentSources: [], + sourceData: [], + availableSources: [], + configuredSources: [], + serviceTypes: [], + permissionsModal: null, + dataLoading: true, + serverStatuses: null, + }; + + const serverStatuses = [ + { + id: '123', + name: 'my source', + service_type: 'github', + status: { + status: 'this is a thing', + synced_at: '2021-01-25', + error_reason: 1, + }, + }, + ]; + + const serverResponse = { + contentSources, + privateContentSources: contentSources, + serviceTypes: configuredSources, + }; + + beforeEach(() => { + jest.useFakeTimers(); + jest.clearAllMocks(); + mount(); + }); + + it('has expected default values', () => { + expect(SourcesLogic.values).toEqual(defaultValues); + }); + + it('handles unmounting', async () => { + unmount(); + expect(clearInterval).toHaveBeenCalled(); + }); + + describe('actions', () => { + describe('onInitializeSources', () => { + it('sets values', () => { + SourcesLogic.actions.onInitializeSources(serverResponse); + + expect(SourcesLogic.values.contentSources).toEqual(contentSources); + expect(SourcesLogic.values.privateContentSources).toEqual(contentSources); + expect(SourcesLogic.values.serviceTypes).toEqual(configuredSources); + expect(SourcesLogic.values.dataLoading).toEqual(false); + }); + + it('fallbacks', () => { + SourcesLogic.actions.onInitializeSources({ + contentSources, + serviceTypes: undefined as any, + }); + + expect(SourcesLogic.values.serviceTypes).toEqual([]); + expect(SourcesLogic.values.privateContentSources).toEqual([]); + }); + }); + + it('setServerSourceStatuses', () => { + SourcesLogic.actions.setServerSourceStatuses(serverStatuses); + const source = serverStatuses[0]; + + expect(SourcesLogic.values.serverStatuses).toEqual({ + [source.id]: source.status.status, + }); + }); + + it('onSetSearchability', () => { + const id = contentSources[0].id; + const updatedSources = [...contentSources]; + updatedSources[0].searchable = false; + SourcesLogic.actions.onInitializeSources(serverResponse); + SourcesLogic.actions.onSetSearchability(id, false); + + expect(SourcesLogic.values.contentSources).toEqual(updatedSources); + expect(SourcesLogic.values.privateContentSources).toEqual(updatedSources); + }); + + describe('setAddedSource', () => { + it('configured', () => { + const name = contentSources[0].name; + SourcesLogic.actions.setAddedSource(name, false, 'custom'); + + expect(SourcesLogic.values.permissionsModal).toEqual({ + addedSourceName: name, + additionalConfiguration: false, + serviceType: 'custom', + }); + expect(setQueuedSuccessMessage).toHaveBeenCalledWith('Successfully connected source. '); + }); + + it('unconfigured', () => { + const name = contentSources[0].name; + SourcesLogic.actions.setAddedSource(name, true, 'custom'); + + expect(SourcesLogic.values.permissionsModal).toEqual({ + addedSourceName: name, + additionalConfiguration: true, + serviceType: 'custom', + }); + expect(setQueuedSuccessMessage).toHaveBeenCalledWith( + 'Successfully connected source. This source requires additional configuration.' + ); + }); + }); + + it('resetPermissionsModal', () => { + SourcesLogic.actions.resetPermissionsModal(); + + expect(SourcesLogic.values.permissionsModal).toEqual(null); + }); + }); + + describe('listeners', () => { + describe('initializeSources', () => { + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const pollForSourceStatusChangesSpy = jest.spyOn( + SourcesLogic.actions, + 'pollForSourceStatusChanges' + ); + const onInitializeSourcesSpy = jest.spyOn(SourcesLogic.actions, 'onInitializeSources'); + const promise = Promise.resolve(contentSources); + http.get.mockReturnValue(promise); + SourcesLogic.actions.initializeSources(); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources'); + await promise; + expect(pollForSourceStatusChangesSpy).toHaveBeenCalled(); + expect(onInitializeSourcesSpy).toHaveBeenCalledWith(contentSources); + }); + + it('calls API (account)', async () => { + AppLogic.values.isOrganization = false; + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + SourcesLogic.actions.initializeSources(); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources'); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + SourcesLogic.actions.initializeSources(); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('setSourceSearchability', () => { + const id = contentSources[0].id; + + it('calls API and sets values (org)', async () => { + AppLogic.values.isOrganization = true; + const onSetSearchability = jest.spyOn(SourcesLogic.actions, 'onSetSearchability'); + const promise = Promise.resolve(contentSources); + http.put.mockReturnValue(promise); + SourcesLogic.actions.setSourceSearchability(id, true); + + expect(http.put).toHaveBeenCalledWith('/api/workplace_search/org/sources/123/searchable', { + body: JSON.stringify({ searchable: true }), + }); + await promise; + expect(onSetSearchability).toHaveBeenCalledWith(id, true); + }); + + it('calls API (account)', async () => { + AppLogic.values.isOrganization = false; + const promise = Promise.resolve(contentSource); + http.put.mockReturnValue(promise); + SourcesLogic.actions.setSourceSearchability(id, true); + + expect(http.put).toHaveBeenCalledWith( + '/api/workplace_search/account/sources/123/searchable', + { + body: JSON.stringify({ searchable: true }), + } + ); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.put.mockReturnValue(promise); + SourcesLogic.actions.setSourceSearchability(id, true); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); + + describe('pollForSourceStatusChanges', () => { + it('calls API and sets values', async () => { + AppLogic.values.isOrganization = true; + SourcesLogic.actions.setServerSourceStatuses(serverStatuses); + + const setServerSourceStatusesSpy = jest.spyOn( + SourcesLogic.actions, + 'setServerSourceStatuses' + ); + const promise = Promise.resolve(contentSources); + http.get.mockReturnValue(promise); + SourcesLogic.actions.pollForSourceStatusChanges(); + + jest.advanceTimersByTime(POLLING_INTERVAL); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/status'); + await promise; + expect(setServerSourceStatusesSpy).toHaveBeenCalledWith(contentSources); + }); + }); + + it('resetSourcesState', () => { + SourcesLogic.actions.resetSourcesState(); + + expect(clearInterval).toHaveBeenCalled(); + }); + }); + + describe('selectors', () => { + it('availableSources & configuredSources have correct length', () => { + SourcesLogic.actions.onInitializeSources(serverResponse); + + expect(SourcesLogic.values.availableSources).toHaveLength(1); + expect(SourcesLogic.values.configuredSources).toHaveLength(5); + }); + }); + + describe('fetchSourceStatuses', () => { + it('calls API and sets values (org)', async () => { + const setServerSourceStatusesSpy = jest.spyOn( + SourcesLogic.actions, + 'setServerSourceStatuses' + ); + const promise = Promise.resolve(contentSources); + http.get.mockReturnValue(promise); + fetchSourceStatuses(true); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/org/sources/status'); + await promise; + expect(setServerSourceStatusesSpy).toHaveBeenCalledWith(contentSources); + }); + + it('calls API (account)', async () => { + const promise = Promise.resolve(contentSource); + http.get.mockReturnValue(promise); + fetchSourceStatuses(false); + + expect(http.get).toHaveBeenCalledWith('/api/workplace_search/account/sources/status'); + }); + + it('handles error', async () => { + const error = { + response: { + error: 'this is an error', + status: 400, + }, + }; + const promise = Promise.reject(error); + http.get.mockReturnValue(promise); + fetchSourceStatuses(true); + await expectedAsyncError(promise); + + expect(flashAPIErrors).toHaveBeenCalledWith(error); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts index 0a3d047796f49..57e1a97e7bdf6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/sources_logic.ts @@ -12,11 +12,7 @@ import { i18n } from '@kbn/i18n'; import { HttpLogic } from '../../../shared/http'; -import { - flashAPIErrors, - setQueuedSuccessMessage, - clearFlashMessages, -} from '../../../shared/flash_messages'; +import { flashAPIErrors, setQueuedSuccessMessage } from '../../../shared/flash_messages'; import { Connector, ContentSourceDetails, ContentSourceStatus, SourceDataItem } from '../../types'; @@ -40,7 +36,6 @@ export interface ISourcesActions { additionalConfiguration: boolean, serviceType: string ): { addedSourceName: string; additionalConfiguration: boolean; serviceType: string }; - resetFlashMessages(): void; resetPermissionsModal(): void; resetSourcesState(): void; initializeSources(): void; @@ -78,7 +73,7 @@ interface ISourcesServerResponse { } let pollingInterval: number; -const POLLING_INTERVAL = 10000; +export const POLLING_INTERVAL = 10000; export const SourcesLogic = kea>({ path: ['enterprise_search', 'workplace_search', 'sources_logic'], @@ -91,7 +86,6 @@ export const SourcesLogic = kea>( additionalConfiguration: boolean, serviceType: string ) => ({ addedSourceName, additionalConfiguration, serviceType }), - resetFlashMessages: () => true, resetPermissionsModal: () => true, resetSourcesState: () => true, initializeSources: () => true, @@ -238,9 +232,6 @@ export const SourcesLogic = kea>( ].join(' ') ); }, - resetFlashMessages: () => { - clearFlashMessages(); - }, resetSourcesState: () => { clearInterval(pollingInterval); }, @@ -252,7 +243,7 @@ export const SourcesLogic = kea>( }), }); -const fetchSourceStatuses = async (isOrganization: boolean) => { +export const fetchSourceStatuses = async (isOrganization: boolean) => { const route = isOrganization ? '/api/workplace_search/org/sources/status' : '/api/workplace_search/account/sources/status'; @@ -273,7 +264,6 @@ const updateSourcesOnToggle = ( sourceId: string, searchable: boolean ): ContentSourceDetails[] => { - if (!contentSources) return []; const sources = cloneDeep(contentSources) as ContentSourceDetails[]; const index = findIndex(sources, ({ id }) => id === sourceId); const updatedSource = sources[index]; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx index 7c746f75ffc94..30f345d8e017e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/groups/groups.test.tsx @@ -7,7 +7,7 @@ import '../../../__mocks__/shallow_useeffect.mock'; import { setMockActions, setMockValues } from '../../../__mocks__'; import { groups } from '../../__mocks__/groups.mock'; -import { mockMeta } from '../../__mocks__/meta.mock'; +import { meta } from '../../__mocks__/meta.mock'; import React from 'react'; import { shallow } from 'enzyme'; @@ -46,7 +46,7 @@ const mockValues = { newGroup: null, groupListLoading: false, hasFiltersSet: false, - groupsMeta: mockMeta, + groupsMeta: meta, filteredSources: [], filteredUsers: [], filterValue: '', From b35a4e645d2ae2a4f44a8e127bd75d4b53e2ab50 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 26 Jan 2021 17:36:44 +0100 Subject: [PATCH 55/90] [ML] Fix swim lane time selection with a single time point and the Watcher URL (#89125) * [ML] fix swim lane selected times with only start boundaries * [ML] unit test * [ML] update url variables * [ML] selectedLanes to an array type * [ML] handle legacy query params --- .../ml/common/types/ml_url_generator.ts | 13 ++++++- .../explorer/hooks/use_selected_cells.test.ts | 34 +++++++++++++++++ .../explorer/hooks/use_selected_cells.ts | 37 +++++++++++++------ .../components/create_watch_flyout/email.html | 2 +- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/ml/common/types/ml_url_generator.ts b/x-pack/plugins/ml/common/types/ml_url_generator.ts index 3ff57fc622da4..d7fded8299952 100644 --- a/x-pack/plugins/ml/common/types/ml_url_generator.ts +++ b/x-pack/plugins/ml/common/types/ml_url_generator.ts @@ -93,7 +93,18 @@ export interface ExplorerAppState { mlExplorerSwimlane: { selectedType?: 'overall' | 'viewBy'; selectedLanes?: string[]; - selectedTimes?: [number, number]; + /** + * @deprecated legacy query param variable, use `selectedLanes` + */ + selectedLane?: string[] | string; + /** + * It's possible to have only "from" time boundaries, e.g. in the Watcher URL + */ + selectedTimes?: [number, number] | number; + /** + * @deprecated legacy query param variable, use `selectedTimes` + */ + selectedTime?: [number, number] | number; showTopFieldValues?: boolean; viewByFieldName?: string; viewByPerPage?: number; diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts index 08c8d11987f19..2308d4ae4f15c 100644 --- a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.test.ts @@ -130,4 +130,38 @@ describe('useSelectedCells', () => { }, }); }); + + test('should extend single time point selection with a bucket interval value', () => { + (useTimefilter() as jest.Mocked).getBounds.mockReturnValue({ + min: moment(1498824778 * 1000), + max: moment(1502366798 * 1000), + }); + + const urlState = { + mlExplorerSwimlane: { + selectedType: 'overall', + selectedLanes: ['Overall'], + selectedTimes: 1498780800, + showTopFieldValues: true, + viewByFieldName: 'apache2.access.remote_ip', + viewByFromPage: 1, + viewByPerPage: 10, + }, + mlExplorerFilter: {}, + } as ExplorerAppState; + + const setUrlState = jest.fn(); + + const bucketInterval = 86400; + + const { result } = renderHook(() => useSelectedCells(urlState, setUrlState, bucketInterval)); + + expect(result.current[0]).toEqual({ + lanes: ['Overall'], + showTopFieldValues: true, + times: [1498780800, 1498867200], + type: 'overall', + viewByFieldName: 'apache2.access.remote_ip', + }); + }); }); diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts index 4ce828b0b7633..becc6197af888 100644 --- a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts @@ -19,18 +19,33 @@ export const useSelectedCells = ( const timeBounds = timeFilter.getBounds(); // keep swimlane selection, restore selectedCells from AppState - const selectedCells = useMemo(() => { - return appState?.mlExplorerSwimlane?.selectedType !== undefined - ? { - type: appState.mlExplorerSwimlane.selectedType, - lanes: appState.mlExplorerSwimlane.selectedLanes!, - times: appState.mlExplorerSwimlane.selectedTimes!, - showTopFieldValues: appState.mlExplorerSwimlane.showTopFieldValues, - viewByFieldName: appState.mlExplorerSwimlane.viewByFieldName, - } - : undefined; + const selectedCells: AppStateSelectedCells | undefined = useMemo(() => { + if (!appState?.mlExplorerSwimlane?.selectedType) { + return; + } + + let times = + appState.mlExplorerSwimlane.selectedTimes ?? appState.mlExplorerSwimlane.selectedTime!; + if (typeof times === 'number' && bucketIntervalInSeconds) { + times = [times, times + bucketIntervalInSeconds]; + } + + let lanes = + appState.mlExplorerSwimlane.selectedLanes ?? appState.mlExplorerSwimlane.selectedLane!; + + if (typeof lanes === 'string') { + lanes = [lanes]; + } + + return { + type: appState.mlExplorerSwimlane.selectedType, + lanes, + times, + showTopFieldValues: appState.mlExplorerSwimlane.showTopFieldValues, + viewByFieldName: appState.mlExplorerSwimlane.viewByFieldName, + } as AppStateSelectedCells; // TODO fix appState to use memoization - }, [JSON.stringify(appState?.mlExplorerSwimlane)]); + }, [JSON.stringify(appState?.mlExplorerSwimlane), bucketIntervalInSeconds]); const setSelectedCells = useCallback( (swimlaneSelectedCells?: AppStateSelectedCells) => { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html index 2e93c7eefcf1e..713a68ba0c036 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/create_watch_flyout/email.html @@ -22,7 +22,7 @@

-
+ <%= openInAnomalyExplorerLinkText %>
From 1f644e44c97ecd6e3a522bcbc4cb25b3ec42c3aa Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 26 Jan 2021 17:42:41 +0100 Subject: [PATCH 56/90] Update babel to v7.12 (#89006) * bump babel version * build kbn-pm * fix integration test * remove cicular dependency between files which crashes Kibana in rutime Co-authored-by: spalger --- package.json | 48 +- packages/kbn-pm/dist/index.js | 926 ++++++++--------- .../reporting/server/lib/layouts/index.ts | 4 +- .../reporting/server/lib/layouts/layout.ts | 4 +- .../server/lib/layouts/preserve_layout.ts | 3 +- .../server/lib/layouts/print_layout.ts | 3 +- yarn.lock | 971 +++++++++++++++++- 7 files changed, 1371 insertions(+), 588 deletions(-) diff --git a/package.json b/package.json index dac83dacf6fbf..d14c5b0a7dc5f 100644 --- a/package.json +++ b/package.json @@ -93,8 +93,8 @@ "yarn": "^1.21.1" }, "dependencies": { - "@babel/core": "^7.11.6", - "@babel/runtime": "^7.11.2", + "@babel/core": "^7.12.10", + "@babel/runtime": "^7.12.5", "@elastic/datemath": "link:packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary", "@elastic/ems-client": "7.11.0", @@ -330,22 +330,22 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/parser": "^7.11.2", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.11.0", - "@babel/preset-env": "^7.11.0", - "@babel/preset-react": "^7.10.4", - "@babel/preset-typescript": "^7.10.4", - "@babel/register": "^7.10.5", - "@babel/traverse": "^7.11.5", - "@babel/types": "^7.11.0", + "@babel/cli": "^7.12.10", + "@babel/parser": "^7.12.11", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-export-namespace-from": "^7.12.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1", + "@babel/plugin-proposal-object-rest-spread": "^7.12.1", + "@babel/plugin-proposal-optional-chaining": "^7.12.7", + "@babel/plugin-proposal-private-methods": "^7.12.1", + "@babel/plugin-transform-modules-commonjs": "^7.12.1", + "@babel/plugin-transform-runtime": "^7.12.10", + "@babel/preset-env": "^7.12.11", + "@babel/preset-react": "^7.12.10", + "@babel/preset-typescript": "^7.12.7", + "@babel/register": "^7.12.10", + "@babel/traverse": "^7.12.12", + "@babel/types": "^7.12.12", "@cypress/snapshot": "^2.1.7", "@cypress/webpack-preprocessor": "^5.5.0", "@elastic/apm-rum": "^5.6.1", @@ -407,7 +407,7 @@ "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", "@types/archiver": "^5.1.0", - "@types/babel__core": "^7.1.10", + "@types/babel__core": "^7.1.12", "@types/base64-js": "^1.2.5", "@types/bluebird": "^3.1.1", "@types/chance": "^1.0.0", @@ -580,10 +580,10 @@ "argsplit": "^1.0.5", "autoprefixer": "^9.7.4", "axe-core": "^4.0.2", - "babel-eslint": "^10.0.3", - "babel-jest": "^26.3.0", - "babel-loader": "^8.0.6", - "babel-plugin-add-module-exports": "^1.0.2", + "babel-eslint": "^10.1.0", + "babel-jest": "^26.6.3", + "babel-loader": "^8.2.2", + "babel-plugin-add-module-exports": "^1.0.4", "babel-plugin-istanbul": "^6.0.0", "babel-plugin-require-context-hook": "^1.0.0", "babel-plugin-styled-components": "^1.10.7", @@ -629,7 +629,7 @@ "eslint-import-resolver-node": "0.3.2", "eslint-import-resolver-webpack": "0.11.1", "eslint-module-utils": "2.5.0", - "eslint-plugin-babel": "^5.3.0", + "eslint-plugin-babel": "^5.3.1", "eslint-plugin-ban": "^1.4.0", "eslint-plugin-cypress": "^2.11.2", "eslint-plugin-eslint-comments": "^3.2.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 0c74315d0f3fb..09995a9be30a6 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -59381,11 +59381,11 @@ const os = __webpack_require__(121); const pMap = __webpack_require__(513); const arrify = __webpack_require__(508); const globby = __webpack_require__(514); -const hasGlob = __webpack_require__(714); -const cpFile = __webpack_require__(716); -const junk = __webpack_require__(726); -const pFilter = __webpack_require__(727); -const CpyError = __webpack_require__(729); +const hasGlob = __webpack_require__(710); +const cpFile = __webpack_require__(712); +const junk = __webpack_require__(722); +const pFilter = __webpack_require__(723); +const CpyError = __webpack_require__(725); const defaultOptions = { ignoreJunk: true @@ -59633,8 +59633,8 @@ const fs = __webpack_require__(134); const arrayUnion = __webpack_require__(515); const glob = __webpack_require__(147); const fastGlob = __webpack_require__(517); -const dirGlob = __webpack_require__(707); -const gitignore = __webpack_require__(710); +const dirGlob = __webpack_require__(703); +const gitignore = __webpack_require__(706); const DEFAULT_FILTER = () => false; @@ -59885,11 +59885,11 @@ module.exports.generateTasks = pkg.generateTasks; Object.defineProperty(exports, "__esModule", { value: true }); var optionsManager = __webpack_require__(519); var taskManager = __webpack_require__(520); -var reader_async_1 = __webpack_require__(678); -var reader_stream_1 = __webpack_require__(702); -var reader_sync_1 = __webpack_require__(703); -var arrayUtils = __webpack_require__(705); -var streamUtils = __webpack_require__(706); +var reader_async_1 = __webpack_require__(674); +var reader_stream_1 = __webpack_require__(698); +var reader_sync_1 = __webpack_require__(699); +var arrayUtils = __webpack_require__(701); +var streamUtils = __webpack_require__(702); /** * Synchronous API. */ @@ -60470,16 +60470,16 @@ module.exports.win32 = win32; var util = __webpack_require__(112); var braces = __webpack_require__(526); var toRegex = __webpack_require__(527); -var extend = __webpack_require__(644); +var extend = __webpack_require__(640); /** * Local dependencies */ -var compilers = __webpack_require__(646); -var parsers = __webpack_require__(673); -var cache = __webpack_require__(674); -var utils = __webpack_require__(675); +var compilers = __webpack_require__(642); +var parsers = __webpack_require__(669); +var cache = __webpack_require__(670); +var utils = __webpack_require__(671); var MAX_LENGTH = 1024 * 64; /** @@ -61360,8 +61360,8 @@ var extend = __webpack_require__(550); */ var compilers = __webpack_require__(552); -var parsers = __webpack_require__(567); -var Braces = __webpack_require__(571); +var parsers = __webpack_require__(565); +var Braces = __webpack_require__(569); var utils = __webpack_require__(553); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -63801,7 +63801,7 @@ utils.extend = __webpack_require__(550); utils.flatten = __webpack_require__(557); utils.isObject = __webpack_require__(535); utils.fillRange = __webpack_require__(558); -utils.repeat = __webpack_require__(566); +utils.repeat = __webpack_require__(564); utils.unique = __webpack_require__(549); utils.define = function(obj, key, val) { @@ -64444,9 +64444,9 @@ function flat(arr, res) { var util = __webpack_require__(112); var isNumber = __webpack_require__(559); -var extend = __webpack_require__(562); -var repeat = __webpack_require__(564); -var toRegex = __webpack_require__(565); +var extend = __webpack_require__(550); +var repeat = __webpack_require__(562); +var toRegex = __webpack_require__(563); /** * Return a range of numbers or letters. @@ -64825,66 +64825,6 @@ function isSlowBuffer (obj) { /* 562 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - - -var isObject = __webpack_require__(563); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; - - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; - -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} - -/** - * Returns true if the given `key` is an own property of `obj`. - */ - -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - - -/***/ }), -/* 563 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 564 */ -/***/ (function(module, exports, __webpack_require__) { - "use strict"; /*! * repeat-string @@ -64959,7 +64899,7 @@ function repeat(str, num) { /***/ }), -/* 565 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64972,7 +64912,7 @@ function repeat(str, num) { -var repeat = __webpack_require__(564); +var repeat = __webpack_require__(562); var isNumber = __webpack_require__(559); var cache = {}; @@ -65260,7 +65200,7 @@ module.exports = toRegexRange; /***/ }), -/* 566 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65285,13 +65225,13 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 567 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(568); +var Node = __webpack_require__(566); var utils = __webpack_require__(553); /** @@ -65652,15 +65592,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 568 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var isObject = __webpack_require__(535); -var define = __webpack_require__(569); -var utils = __webpack_require__(570); +var define = __webpack_require__(567); +var utils = __webpack_require__(568); var ownNames; /** @@ -66151,7 +66091,7 @@ exports = module.exports = Node; /***/ }), -/* 569 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66189,7 +66129,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 570 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67215,16 +67155,16 @@ function assert(val, message) { /***/ }), -/* 571 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var extend = __webpack_require__(550); -var Snapdragon = __webpack_require__(572); +var Snapdragon = __webpack_require__(570); var compilers = __webpack_require__(552); -var parsers = __webpack_require__(567); +var parsers = __webpack_require__(565); var utils = __webpack_require__(553); /** @@ -67326,17 +67266,17 @@ module.exports = Braces; /***/ }), -/* 572 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(573); -var define = __webpack_require__(602); -var Compiler = __webpack_require__(612); -var Parser = __webpack_require__(641); -var utils = __webpack_require__(621); +var Base = __webpack_require__(571); +var define = __webpack_require__(598); +var Compiler = __webpack_require__(608); +var Parser = __webpack_require__(637); +var utils = __webpack_require__(617); var regexCache = {}; var cache = {}; @@ -67507,20 +67447,20 @@ module.exports.Parser = Parser; /***/ }), -/* 573 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var define = __webpack_require__(574); -var CacheBase = __webpack_require__(575); -var Emitter = __webpack_require__(576); +var define = __webpack_require__(572); +var CacheBase = __webpack_require__(573); +var Emitter = __webpack_require__(574); var isObject = __webpack_require__(535); -var merge = __webpack_require__(596); -var pascal = __webpack_require__(599); -var cu = __webpack_require__(600); +var merge = __webpack_require__(592); +var pascal = __webpack_require__(595); +var cu = __webpack_require__(596); /** * Optionally define a custom `cache` namespace to use. @@ -67949,7 +67889,7 @@ module.exports.namespace = namespace; /***/ }), -/* 574 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67987,21 +67927,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 575 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var isObject = __webpack_require__(535); -var Emitter = __webpack_require__(576); -var visit = __webpack_require__(577); -var toPath = __webpack_require__(580); -var union = __webpack_require__(581); -var del = __webpack_require__(587); -var get = __webpack_require__(584); -var has = __webpack_require__(592); -var set = __webpack_require__(595); +var Emitter = __webpack_require__(574); +var visit = __webpack_require__(575); +var toPath = __webpack_require__(578); +var union = __webpack_require__(579); +var del = __webpack_require__(583); +var get = __webpack_require__(581); +var has = __webpack_require__(588); +var set = __webpack_require__(591); /** * Create a `Cache` constructor that when instantiated will @@ -68255,7 +68195,7 @@ module.exports.namespace = namespace; /***/ }), -/* 576 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { @@ -68424,7 +68364,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 577 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68437,8 +68377,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(578); -var mapVisit = __webpack_require__(579); +var visit = __webpack_require__(576); +var mapVisit = __webpack_require__(577); module.exports = function(collection, method, val) { var result; @@ -68461,7 +68401,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 578 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68501,14 +68441,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 579 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var visit = __webpack_require__(578); +var visit = __webpack_require__(576); /** * Map `visit` over an array of objects. @@ -68545,7 +68485,7 @@ function isObject(val) { /***/ }), -/* 580 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68585,16 +68525,16 @@ function filter(arr) { /***/ }), -/* 581 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(582); -var union = __webpack_require__(583); -var get = __webpack_require__(584); -var set = __webpack_require__(585); +var isObject = __webpack_require__(551); +var union = __webpack_require__(580); +var get = __webpack_require__(581); +var set = __webpack_require__(582); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -68622,27 +68562,7 @@ function arrayify(val) { /***/ }), -/* 582 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function isExtendable(val) { - return typeof val !== 'undefined' && val !== null - && (typeof val === 'object' || typeof val === 'function'); -}; - - -/***/ }), -/* 583 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68678,7 +68598,7 @@ module.exports = function union(init) { /***/ }), -/* 584 */ +/* 581 */ /***/ (function(module, exports) { /*! @@ -68734,7 +68654,7 @@ function toString(val) { /***/ }), -/* 585 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68748,9 +68668,9 @@ function toString(val) { var split = __webpack_require__(554); -var extend = __webpack_require__(586); +var extend = __webpack_require__(550); var isPlainObject = __webpack_require__(544); -var isObject = __webpack_require__(582); +var isObject = __webpack_require__(551); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -68796,47 +68716,7 @@ function isValidKey(key) { /***/ }), -/* 586 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isObject = __webpack_require__(582); - -module.exports = function extend(o/*, objects*/) { - if (!isObject(o)) { o = {}; } - - var len = arguments.length; - for (var i = 1; i < len; i++) { - var obj = arguments[i]; - - if (isObject(obj)) { - assign(o, obj); - } - } - return o; -}; - -function assign(a, b) { - for (var key in b) { - if (hasOwn(b, key)) { - a[key] = b[key]; - } - } -} - -/** - * Returns true if the given `key` is an own property of `obj`. - */ - -function hasOwn(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); -} - - -/***/ }), -/* 587 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68850,7 +68730,7 @@ function hasOwn(obj, key) { var isObject = __webpack_require__(535); -var has = __webpack_require__(588); +var has = __webpack_require__(584); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -68875,7 +68755,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 588 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68888,9 +68768,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(589); -var hasValues = __webpack_require__(591); -var get = __webpack_require__(584); +var isObject = __webpack_require__(585); +var hasValues = __webpack_require__(587); +var get = __webpack_require__(581); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -68901,7 +68781,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 589 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68914,7 +68794,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(590); +var isArray = __webpack_require__(586); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -68922,7 +68802,7 @@ module.exports = function isObject(val) { /***/ }), -/* 590 */ +/* 586 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -68933,7 +68813,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 591 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68976,7 +68856,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 592 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -68990,8 +68870,8 @@ module.exports = function hasValue(o, noZero) { var isObject = __webpack_require__(535); -var hasValues = __webpack_require__(593); -var get = __webpack_require__(584); +var hasValues = __webpack_require__(589); +var get = __webpack_require__(581); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -68999,7 +68879,7 @@ module.exports = function(val, prop) { /***/ }), -/* 593 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69012,7 +68892,7 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(594); +var typeOf = __webpack_require__(590); var isNumber = __webpack_require__(559); module.exports = function hasValue(val) { @@ -69066,7 +68946,7 @@ module.exports = function hasValue(val) { /***/ }), -/* 594 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { var isBuffer = __webpack_require__(561); @@ -69191,7 +69071,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 595 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69205,9 +69085,9 @@ module.exports = function kindOf(val) { var split = __webpack_require__(554); -var extend = __webpack_require__(586); +var extend = __webpack_require__(550); var isPlainObject = __webpack_require__(544); -var isObject = __webpack_require__(582); +var isObject = __webpack_require__(551); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -69253,14 +69133,14 @@ function isValidKey(key) { /***/ }), -/* 596 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(597); -var forIn = __webpack_require__(598); +var isExtendable = __webpack_require__(593); +var forIn = __webpack_require__(594); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -69324,7 +69204,7 @@ module.exports = mixinDeep; /***/ }), -/* 597 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69345,7 +69225,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 598 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69368,7 +69248,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 599 */ +/* 595 */ /***/ (function(module, exports) { /*! @@ -69395,14 +69275,14 @@ module.exports = pascalcase; /***/ }), -/* 600 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(112); -var utils = __webpack_require__(601); +var utils = __webpack_require__(597); /** * Expose class utils @@ -69767,7 +69647,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 601 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69781,10 +69661,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(583); -utils.define = __webpack_require__(602); +utils.union = __webpack_require__(580); +utils.define = __webpack_require__(598); utils.isObj = __webpack_require__(535); -utils.staticExtend = __webpack_require__(609); +utils.staticExtend = __webpack_require__(605); /** @@ -69795,7 +69675,7 @@ module.exports = utils; /***/ }), -/* 602 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69808,7 +69688,7 @@ module.exports = utils; -var isDescriptor = __webpack_require__(603); +var isDescriptor = __webpack_require__(599); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -69833,7 +69713,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 603 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69846,9 +69726,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(604); -var isAccessor = __webpack_require__(605); -var isData = __webpack_require__(607); +var typeOf = __webpack_require__(600); +var isAccessor = __webpack_require__(601); +var isData = __webpack_require__(603); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -69862,7 +69742,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 604 */ +/* 600 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -70015,7 +69895,7 @@ function isBuffer(val) { /***/ }), -/* 605 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70028,7 +69908,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(606); +var typeOf = __webpack_require__(602); // accessor descriptor properties var accessor = { @@ -70091,7 +69971,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 606 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { var isBuffer = __webpack_require__(561); @@ -70213,7 +70093,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 607 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70226,7 +70106,7 @@ module.exports = function kindOf(val) { -var typeOf = __webpack_require__(608); +var typeOf = __webpack_require__(604); // data descriptor properties var data = { @@ -70275,7 +70155,7 @@ module.exports = isDataDescriptor; /***/ }), -/* 608 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { var isBuffer = __webpack_require__(561); @@ -70397,7 +70277,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 609 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70410,8 +70290,8 @@ module.exports = function kindOf(val) { -var copy = __webpack_require__(610); -var define = __webpack_require__(602); +var copy = __webpack_require__(606); +var define = __webpack_require__(598); var util = __webpack_require__(112); /** @@ -70494,15 +70374,15 @@ module.exports = extend; /***/ }), -/* 610 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var typeOf = __webpack_require__(560); -var copyDescriptor = __webpack_require__(611); -var define = __webpack_require__(602); +var copyDescriptor = __webpack_require__(607); +var define = __webpack_require__(598); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -70675,7 +70555,7 @@ module.exports.has = has; /***/ }), -/* 611 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70763,16 +70643,16 @@ function isObject(val) { /***/ }), -/* 612 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(613); -var define = __webpack_require__(602); -var debug = __webpack_require__(615)('snapdragon:compiler'); -var utils = __webpack_require__(621); +var use = __webpack_require__(609); +var define = __webpack_require__(598); +var debug = __webpack_require__(611)('snapdragon:compiler'); +var utils = __webpack_require__(617); /** * Create a new `Compiler` with the given `options`. @@ -70926,7 +70806,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(640); + var sourcemaps = __webpack_require__(636); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -70947,7 +70827,7 @@ module.exports = Compiler; /***/ }), -/* 613 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70960,7 +70840,7 @@ module.exports = Compiler; -var utils = __webpack_require__(614); +var utils = __webpack_require__(610); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -71075,7 +70955,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 614 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71089,7 +70969,7 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(602); +utils.define = __webpack_require__(598); utils.isObject = __webpack_require__(535); @@ -71105,7 +70985,7 @@ module.exports = utils; /***/ }), -/* 615 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -71114,14 +70994,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(616); + module.exports = __webpack_require__(612); } else { - module.exports = __webpack_require__(619); + module.exports = __webpack_require__(615); } /***/ }), -/* 616 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -71130,7 +71010,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(617); +exports = module.exports = __webpack_require__(613); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -71312,7 +71192,7 @@ function localstorage() { /***/ }), -/* 617 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { @@ -71328,7 +71208,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(618); +exports.humanize = __webpack_require__(614); /** * The currently active debug mode names, and names to skip. @@ -71520,7 +71400,7 @@ function coerce(val) { /***/ }), -/* 618 */ +/* 614 */ /***/ (function(module, exports) { /** @@ -71678,7 +71558,7 @@ function plural(ms, n, name) { /***/ }), -/* 619 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -71694,7 +71574,7 @@ var util = __webpack_require__(112); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(617); +exports = module.exports = __webpack_require__(613); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -71873,7 +71753,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(620); + var net = __webpack_require__(616); stream = new net.Socket({ fd: fd, readable: false, @@ -71932,13 +71812,13 @@ exports.enable(load()); /***/ }), -/* 620 */ +/* 616 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 621 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71948,9 +71828,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(586); -exports.SourceMap = __webpack_require__(622); -exports.sourceMapResolve = __webpack_require__(633); +exports.extend = __webpack_require__(550); +exports.SourceMap = __webpack_require__(618); +exports.sourceMapResolve = __webpack_require__(629); /** * Convert backslash in the given string to forward slashes @@ -71993,7 +71873,7 @@ exports.last = function(arr, n) { /***/ }), -/* 622 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -72001,13 +71881,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(623).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(629).SourceMapConsumer; -exports.SourceNode = __webpack_require__(632).SourceNode; +exports.SourceMapGenerator = __webpack_require__(619).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(625).SourceMapConsumer; +exports.SourceNode = __webpack_require__(628).SourceNode; /***/ }), -/* 623 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72017,10 +71897,10 @@ exports.SourceNode = __webpack_require__(632).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(624); -var util = __webpack_require__(626); -var ArraySet = __webpack_require__(627).ArraySet; -var MappingList = __webpack_require__(628).MappingList; +var base64VLQ = __webpack_require__(620); +var util = __webpack_require__(622); +var ArraySet = __webpack_require__(623).ArraySet; +var MappingList = __webpack_require__(624).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -72429,7 +72309,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 624 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72469,7 +72349,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(625); +var base64 = __webpack_require__(621); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -72575,7 +72455,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 625 */ +/* 621 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -72648,7 +72528,7 @@ exports.decode = function (charCode) { /***/ }), -/* 626 */ +/* 622 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73071,7 +72951,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 627 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73081,7 +72961,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(626); +var util = __webpack_require__(622); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -73198,7 +73078,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 628 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73208,7 +73088,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(626); +var util = __webpack_require__(622); /** * Determine whether mappingB is after mappingA with respect to generated @@ -73283,7 +73163,7 @@ exports.MappingList = MappingList; /***/ }), -/* 629 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -73293,11 +73173,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(626); -var binarySearch = __webpack_require__(630); -var ArraySet = __webpack_require__(627).ArraySet; -var base64VLQ = __webpack_require__(624); -var quickSort = __webpack_require__(631).quickSort; +var util = __webpack_require__(622); +var binarySearch = __webpack_require__(626); +var ArraySet = __webpack_require__(623).ArraySet; +var base64VLQ = __webpack_require__(620); +var quickSort = __webpack_require__(627).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -74371,7 +74251,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 630 */ +/* 626 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74488,7 +74368,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 631 */ +/* 627 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74608,7 +74488,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 632 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -74618,8 +74498,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(623).SourceMapGenerator; -var util = __webpack_require__(626); +var SourceMapGenerator = __webpack_require__(619).SourceMapGenerator; +var util = __webpack_require__(622); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -75027,17 +74907,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 633 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(634) -var resolveUrl = __webpack_require__(635) -var decodeUriComponent = __webpack_require__(636) -var urix = __webpack_require__(638) -var atob = __webpack_require__(639) +var sourceMappingURL = __webpack_require__(630) +var resolveUrl = __webpack_require__(631) +var decodeUriComponent = __webpack_require__(632) +var urix = __webpack_require__(634) +var atob = __webpack_require__(635) @@ -75335,7 +75215,7 @@ module.exports = { /***/ }), -/* 634 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -75398,7 +75278,7 @@ void (function(root, factory) { /***/ }), -/* 635 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -75416,13 +75296,13 @@ module.exports = resolveUrl /***/ }), -/* 636 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(637) +var decodeUriComponent = __webpack_require__(633) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -75433,7 +75313,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 637 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75534,7 +75414,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 638 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -75557,7 +75437,7 @@ module.exports = urix /***/ }), -/* 639 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75571,7 +75451,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 640 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75579,8 +75459,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(134); var path = __webpack_require__(4); -var define = __webpack_require__(602); -var utils = __webpack_require__(621); +var define = __webpack_require__(598); +var utils = __webpack_require__(617); /** * Expose `mixin()`. @@ -75723,19 +75603,19 @@ exports.comment = function(node) { /***/ }), -/* 641 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(613); +var use = __webpack_require__(609); var util = __webpack_require__(112); -var Cache = __webpack_require__(642); -var define = __webpack_require__(602); -var debug = __webpack_require__(615)('snapdragon:parser'); -var Position = __webpack_require__(643); -var utils = __webpack_require__(621); +var Cache = __webpack_require__(638); +var define = __webpack_require__(598); +var debug = __webpack_require__(611)('snapdragon:parser'); +var Position = __webpack_require__(639); +var utils = __webpack_require__(617); /** * Create a new `Parser` with the given `input` and `options`. @@ -76263,7 +76143,7 @@ module.exports = Parser; /***/ }), -/* 642 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76370,13 +76250,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 643 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(602); +var define = __webpack_require__(598); /** * Store position for a node @@ -76391,13 +76271,13 @@ module.exports = function Position(start, parser) { /***/ }), -/* 644 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(645); +var isExtendable = __webpack_require__(641); var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { @@ -76458,7 +76338,7 @@ function isEnum(obj, key) { /***/ }), -/* 645 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76479,14 +76359,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 646 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(647); -var extglob = __webpack_require__(662); +var nanomatch = __webpack_require__(643); +var extglob = __webpack_require__(658); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -76563,7 +76443,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 647 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76575,16 +76455,16 @@ function escapeExtglobs(compiler) { var util = __webpack_require__(112); var toRegex = __webpack_require__(527); -var extend = __webpack_require__(648); +var extend = __webpack_require__(644); /** * Local dependencies */ -var compilers = __webpack_require__(650); -var parsers = __webpack_require__(651); -var cache = __webpack_require__(654); -var utils = __webpack_require__(656); +var compilers = __webpack_require__(646); +var parsers = __webpack_require__(647); +var cache = __webpack_require__(650); +var utils = __webpack_require__(652); var MAX_LENGTH = 1024 * 64; /** @@ -77408,13 +77288,13 @@ module.exports = nanomatch; /***/ }), -/* 648 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(649); +var isExtendable = __webpack_require__(645); var assignSymbols = __webpack_require__(545); module.exports = Object.assign || function(obj/*, objects*/) { @@ -77475,7 +77355,7 @@ function isEnum(obj, key) { /***/ }), -/* 649 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77496,7 +77376,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 650 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77842,7 +77722,7 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 651 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77850,7 +77730,7 @@ module.exports = function(nanomatch, options) { var regexNot = __webpack_require__(546); var toRegex = __webpack_require__(527); -var isOdd = __webpack_require__(652); +var isOdd = __webpack_require__(648); /** * Characters to use in negation regex (we want to "not" match @@ -78236,7 +78116,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 652 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78249,7 +78129,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(653); +var isNumber = __webpack_require__(649); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -78263,7 +78143,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 653 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78291,14 +78171,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 654 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(655))(); +module.exports = new (__webpack_require__(651))(); /***/ }), -/* 655 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78311,7 +78191,7 @@ module.exports = new (__webpack_require__(655))(); -var MapCache = __webpack_require__(642); +var MapCache = __webpack_require__(638); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -78433,7 +78313,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 656 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78446,13 +78326,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(657)(); -var Snapdragon = __webpack_require__(572); -utils.define = __webpack_require__(658); -utils.diff = __webpack_require__(659); -utils.extend = __webpack_require__(648); -utils.pick = __webpack_require__(660); -utils.typeOf = __webpack_require__(661); +var isWindows = __webpack_require__(653)(); +var Snapdragon = __webpack_require__(570); +utils.define = __webpack_require__(654); +utils.diff = __webpack_require__(655); +utils.extend = __webpack_require__(644); +utils.pick = __webpack_require__(656); +utils.typeOf = __webpack_require__(657); utils.unique = __webpack_require__(549); /** @@ -78819,7 +78699,7 @@ utils.unixify = function(options) { /***/ }), -/* 657 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -78847,7 +78727,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 658 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78892,7 +78772,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 659 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78946,7 +78826,7 @@ function diffArray(one, two) { /***/ }), -/* 660 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78988,7 +78868,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 661 */ +/* 657 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -79123,7 +79003,7 @@ function isBuffer(val) { /***/ }), -/* 662 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79133,7 +79013,7 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(586); +var extend = __webpack_require__(550); var unique = __webpack_require__(549); var toRegex = __webpack_require__(527); @@ -79141,10 +79021,10 @@ var toRegex = __webpack_require__(527); * Local dependencies */ -var compilers = __webpack_require__(663); -var parsers = __webpack_require__(669); -var Extglob = __webpack_require__(672); -var utils = __webpack_require__(671); +var compilers = __webpack_require__(659); +var parsers = __webpack_require__(665); +var Extglob = __webpack_require__(668); +var utils = __webpack_require__(667); var MAX_LENGTH = 1024 * 64; /** @@ -79461,13 +79341,13 @@ module.exports = extglob; /***/ }), -/* 663 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(664); +var brackets = __webpack_require__(660); /** * Extglob compilers @@ -79637,7 +79517,7 @@ module.exports = function(extglob) { /***/ }), -/* 664 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79647,16 +79527,16 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(665); -var parsers = __webpack_require__(667); +var compilers = __webpack_require__(661); +var parsers = __webpack_require__(663); /** * Module dependencies */ -var debug = __webpack_require__(615)('expand-brackets'); -var extend = __webpack_require__(586); -var Snapdragon = __webpack_require__(572); +var debug = __webpack_require__(611)('expand-brackets'); +var extend = __webpack_require__(550); +var Snapdragon = __webpack_require__(570); var toRegex = __webpack_require__(527); /** @@ -79855,13 +79735,13 @@ module.exports = brackets; /***/ }), -/* 665 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(666); +var posix = __webpack_require__(662); module.exports = function(brackets) { brackets.compiler @@ -79949,7 +79829,7 @@ module.exports = function(brackets) { /***/ }), -/* 666 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79978,14 +79858,14 @@ module.exports = { /***/ }), -/* 667 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(668); -var define = __webpack_require__(602); +var utils = __webpack_require__(664); +var define = __webpack_require__(598); /** * Text regex @@ -80204,7 +80084,7 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 668 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80245,15 +80125,15 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 669 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(664); -var define = __webpack_require__(670); -var utils = __webpack_require__(671); +var brackets = __webpack_require__(660); +var define = __webpack_require__(666); +var utils = __webpack_require__(667); /** * Characters to use in text regex (we want to "not" match @@ -80408,7 +80288,7 @@ module.exports = parsers; /***/ }), -/* 670 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80446,14 +80326,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 671 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var regex = __webpack_require__(546); -var Cache = __webpack_require__(655); +var Cache = __webpack_require__(651); /** * Utils @@ -80522,7 +80402,7 @@ utils.createRegex = function(str) { /***/ }), -/* 672 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80532,16 +80412,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(572); -var define = __webpack_require__(670); -var extend = __webpack_require__(586); +var Snapdragon = __webpack_require__(570); +var define = __webpack_require__(666); +var extend = __webpack_require__(550); /** * Local dependencies */ -var compilers = __webpack_require__(663); -var parsers = __webpack_require__(669); +var compilers = __webpack_require__(659); +var parsers = __webpack_require__(665); /** * Customize Snapdragon parser and renderer @@ -80607,14 +80487,14 @@ module.exports = Extglob; /***/ }), -/* 673 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(662); -var nanomatch = __webpack_require__(647); +var extglob = __webpack_require__(658); +var nanomatch = __webpack_require__(643); var regexNot = __webpack_require__(546); var toRegex = __webpack_require__(527); var not; @@ -80697,14 +80577,14 @@ function textRegex(pattern) { /***/ }), -/* 674 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(655))(); +module.exports = new (__webpack_require__(651))(); /***/ }), -/* 675 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80717,12 +80597,12 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(572); -utils.define = __webpack_require__(676); -utils.diff = __webpack_require__(659); -utils.extend = __webpack_require__(644); -utils.pick = __webpack_require__(660); -utils.typeOf = __webpack_require__(677); +var Snapdragon = __webpack_require__(570); +utils.define = __webpack_require__(672); +utils.diff = __webpack_require__(655); +utils.extend = __webpack_require__(640); +utils.pick = __webpack_require__(656); +utils.typeOf = __webpack_require__(673); utils.unique = __webpack_require__(549); /** @@ -81020,7 +80900,7 @@ utils.unixify = function(options) { /***/ }), -/* 676 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81065,7 +80945,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 677 */ +/* 673 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -81200,7 +81080,7 @@ function isBuffer(val) { /***/ }), -/* 678 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81219,9 +81099,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(679); -var reader_1 = __webpack_require__(692); -var fs_stream_1 = __webpack_require__(696); +var readdir = __webpack_require__(675); +var reader_1 = __webpack_require__(688); +var fs_stream_1 = __webpack_require__(692); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -81282,15 +81162,15 @@ exports.default = ReaderAsync; /***/ }), -/* 679 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(680); -const readdirAsync = __webpack_require__(688); -const readdirStream = __webpack_require__(691); +const readdirSync = __webpack_require__(676); +const readdirAsync = __webpack_require__(684); +const readdirStream = __webpack_require__(687); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -81374,7 +81254,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 680 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81382,11 +81262,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(681); +const DirectoryReader = __webpack_require__(677); let syncFacade = { - fs: __webpack_require__(686), - forEach: __webpack_require__(687), + fs: __webpack_require__(682), + forEach: __webpack_require__(683), sync: true }; @@ -81415,7 +81295,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 681 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81424,9 +81304,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(138).Readable; const EventEmitter = __webpack_require__(156).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(682); -const stat = __webpack_require__(684); -const call = __webpack_require__(685); +const normalizeOptions = __webpack_require__(678); +const stat = __webpack_require__(680); +const call = __webpack_require__(681); /** * Asynchronously reads the contents of a directory and streams the results @@ -81802,14 +81682,14 @@ module.exports = DirectoryReader; /***/ }), -/* 682 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(683); +const globToRegExp = __webpack_require__(679); module.exports = normalizeOptions; @@ -81986,7 +81866,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 683 */ +/* 679 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -82123,13 +82003,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 684 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(685); +const call = __webpack_require__(681); module.exports = stat; @@ -82204,7 +82084,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 685 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82265,14 +82145,14 @@ function callOnce (fn) { /***/ }), -/* 686 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const call = __webpack_require__(685); +const call = __webpack_require__(681); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -82336,7 +82216,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 687 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82365,7 +82245,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 688 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82373,12 +82253,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(689); -const DirectoryReader = __webpack_require__(681); +const maybe = __webpack_require__(685); +const DirectoryReader = __webpack_require__(677); let asyncFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(690), + forEach: __webpack_require__(686), async: true }; @@ -82420,7 +82300,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 689 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82447,7 +82327,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 690 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82483,7 +82363,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 691 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82491,11 +82371,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(681); +const DirectoryReader = __webpack_require__(677); let streamFacade = { fs: __webpack_require__(134), - forEach: __webpack_require__(690), + forEach: __webpack_require__(686), async: true }; @@ -82515,16 +82395,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 692 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(693); -var entry_1 = __webpack_require__(695); -var pathUtil = __webpack_require__(694); +var deep_1 = __webpack_require__(689); +var entry_1 = __webpack_require__(691); +var pathUtil = __webpack_require__(690); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -82590,13 +82470,13 @@ exports.default = Reader; /***/ }), -/* 693 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(694); +var pathUtils = __webpack_require__(690); var patternUtils = __webpack_require__(521); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { @@ -82680,7 +82560,7 @@ exports.default = DeepFilter; /***/ }), -/* 694 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82711,13 +82591,13 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 695 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(694); +var pathUtils = __webpack_require__(690); var patternUtils = __webpack_require__(521); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { @@ -82803,7 +82683,7 @@ exports.default = EntryFilter; /***/ }), -/* 696 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82823,8 +82703,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(138); -var fsStat = __webpack_require__(697); -var fs_1 = __webpack_require__(701); +var fsStat = __webpack_require__(693); +var fs_1 = __webpack_require__(697); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -82874,14 +82754,14 @@ exports.default = FileSystemStream; /***/ }), -/* 697 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(698); -const statProvider = __webpack_require__(700); +const optionsManager = __webpack_require__(694); +const statProvider = __webpack_require__(696); /** * Asynchronous API. */ @@ -82912,13 +82792,13 @@ exports.statSync = statSync; /***/ }), -/* 698 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(699); +const fsAdapter = __webpack_require__(695); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -82931,7 +82811,7 @@ exports.prepare = prepare; /***/ }), -/* 699 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -82954,7 +82834,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 700 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83006,7 +82886,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 701 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83037,7 +82917,7 @@ exports.default = FileSystem; /***/ }), -/* 702 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83057,9 +82937,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(138); -var readdir = __webpack_require__(679); -var reader_1 = __webpack_require__(692); -var fs_stream_1 = __webpack_require__(696); +var readdir = __webpack_require__(675); +var reader_1 = __webpack_require__(688); +var fs_stream_1 = __webpack_require__(692); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -83127,7 +83007,7 @@ exports.default = ReaderStream; /***/ }), -/* 703 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83146,9 +83026,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(679); -var reader_1 = __webpack_require__(692); -var fs_sync_1 = __webpack_require__(704); +var readdir = __webpack_require__(675); +var reader_1 = __webpack_require__(688); +var fs_sync_1 = __webpack_require__(700); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -83208,7 +83088,7 @@ exports.default = ReaderSync; /***/ }), -/* 704 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83227,8 +83107,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(697); -var fs_1 = __webpack_require__(701); +var fsStat = __webpack_require__(693); +var fs_1 = __webpack_require__(697); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -83274,7 +83154,7 @@ exports.default = FileSystemSync; /***/ }), -/* 705 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83290,7 +83170,7 @@ exports.flatten = flatten; /***/ }), -/* 706 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83311,13 +83191,13 @@ exports.merge = merge; /***/ }), -/* 707 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(708); +const pathType = __webpack_require__(704); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -83383,13 +83263,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 708 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const pify = __webpack_require__(709); +const pify = __webpack_require__(705); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -83432,7 +83312,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 709 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83523,7 +83403,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 710 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83531,9 +83411,9 @@ module.exports = (obj, opts) => { const fs = __webpack_require__(134); const path = __webpack_require__(4); const fastGlob = __webpack_require__(517); -const gitIgnore = __webpack_require__(711); -const pify = __webpack_require__(712); -const slash = __webpack_require__(713); +const gitIgnore = __webpack_require__(707); +const pify = __webpack_require__(708); +const slash = __webpack_require__(709); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -83631,7 +83511,7 @@ module.exports.sync = options => { /***/ }), -/* 711 */ +/* 707 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -84100,7 +83980,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 712 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84175,7 +84055,7 @@ module.exports = (input, options) => { /***/ }), -/* 713 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84193,7 +84073,7 @@ module.exports = input => { /***/ }), -/* 714 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84206,7 +84086,7 @@ module.exports = input => { -var isGlob = __webpack_require__(715); +var isGlob = __webpack_require__(711); module.exports = function hasGlob(val) { if (val == null) return false; @@ -84226,7 +84106,7 @@ module.exports = function hasGlob(val) { /***/ }), -/* 715 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -84257,17 +84137,17 @@ module.exports = function isGlob(str) { /***/ }), -/* 716 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(134); -const pEvent = __webpack_require__(717); -const CpFileError = __webpack_require__(720); -const fs = __webpack_require__(722); -const ProgressEmitter = __webpack_require__(725); +const pEvent = __webpack_require__(713); +const CpFileError = __webpack_require__(716); +const fs = __webpack_require__(718); +const ProgressEmitter = __webpack_require__(721); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -84381,12 +84261,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 717 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(718); +const pTimeout = __webpack_require__(714); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -84677,12 +84557,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 718 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(719); +const pFinally = __webpack_require__(715); class TimeoutError extends Error { constructor(message) { @@ -84728,7 +84608,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 719 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84750,12 +84630,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 720 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(721); +const NestedError = __webpack_require__(717); class CpFileError extends NestedError { constructor(message, nested) { @@ -84769,7 +84649,7 @@ module.exports = CpFileError; /***/ }), -/* 721 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(112).inherits; @@ -84825,16 +84705,16 @@ module.exports = NestedError; /***/ }), -/* 722 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const fs = __webpack_require__(133); -const makeDir = __webpack_require__(723); -const pEvent = __webpack_require__(717); -const CpFileError = __webpack_require__(720); +const makeDir = __webpack_require__(719); +const pEvent = __webpack_require__(713); +const CpFileError = __webpack_require__(716); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -84931,7 +84811,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 723 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84939,7 +84819,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(134); const path = __webpack_require__(4); const {promisify} = __webpack_require__(112); -const semver = __webpack_require__(724); +const semver = __webpack_require__(720); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -85094,7 +84974,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 724 */ +/* 720 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -86696,7 +86576,7 @@ function coerce (version, options) { /***/ }), -/* 725 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86737,7 +86617,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 726 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86783,12 +86663,12 @@ exports.default = module.exports; /***/ }), -/* 727 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(728); +const pMap = __webpack_require__(724); const pFilter = async (iterable, filterer, options) => { const values = await pMap( @@ -86805,7 +86685,7 @@ module.exports.default = pFilter; /***/ }), -/* 728 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86884,12 +86764,12 @@ module.exports.default = pMap; /***/ }), -/* 729 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(721); +const NestedError = __webpack_require__(717); class CpyError extends NestedError { constructor(message, nested) { diff --git a/x-pack/plugins/reporting/server/lib/layouts/index.ts b/x-pack/plugins/reporting/server/lib/layouts/index.ts index e0b5b3f095443..b6804b6ac8c8a 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/index.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/index.ts @@ -7,7 +7,7 @@ import { LevelLogger } from '../'; import { LayoutSelectorDictionary, Size } from '../../../common/types'; import { HeadlessChromiumDriver } from '../../browsers'; -import { Layout } from './layout'; +import type { Layout } from './layout'; export { LayoutParams, @@ -17,7 +17,7 @@ export { Size, } from '../../../common/types'; export { createLayout } from './create_layout'; -export { Layout } from './layout'; +export type { Layout } from './layout'; export { PreserveLayout } from './preserve_layout'; export { CanvasLayout } from './canvas_layout'; export { PrintLayout } from './print_layout'; diff --git a/x-pack/plugins/reporting/server/lib/layouts/layout.ts b/x-pack/plugins/reporting/server/lib/layouts/layout.ts index cb068632063a5..027404f4c33e2 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/layout.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CustomPageSize, PredefinedPageSize } from 'pdfmake/interfaces'; -import { PageSizeParams, PdfImageSize, Size } from './'; +import type { CustomPageSize, PredefinedPageSize } from 'pdfmake/interfaces'; +import type { PageSizeParams, PdfImageSize, Size } from '../../../common/types'; export interface ViewZoomWidthHeight { zoom: number; diff --git a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts index ccd08e01fec19..e7c84f2088319 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/preserve_layout.ts @@ -9,7 +9,8 @@ import { CustomPageSize } from 'pdfmake/interfaces'; import { getDefaultLayoutSelectors } from '../../../common'; import { LAYOUT_TYPES } from '../../../common/constants'; import { LayoutSelectorDictionary, PageSizeParams, Size } from '../../../common/types'; -import { Layout, LayoutInstance } from './'; +import type { LayoutInstance } from './'; +import { Layout } from './layout'; // We use a zoom of two to bump up the resolution of the screenshot a bit. const ZOOM: number = 2; diff --git a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts index 8db1fa7ff6347..c51582aa404d9 100644 --- a/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts +++ b/x-pack/plugins/reporting/server/lib/layouts/print_layout.ts @@ -13,7 +13,8 @@ import { LAYOUT_TYPES } from '../../../common/constants'; import { LayoutSelectorDictionary, Size } from '../../../common/types'; import { HeadlessChromiumDriver } from '../../browsers'; import { CaptureConfig } from '../../types'; -import { Layout, LayoutInstance } from './'; +import type { LayoutInstance } from './'; +import { Layout } from './layout'; export class PrintLayout extends Layout implements LayoutInstance { public readonly selectors: LayoutSelectorDictionary = { diff --git a/yarn.lock b/yarn.lock index 7625510d3b915..174f1284a3a6b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@babel/cli@^7.10.5": - version "7.11.6" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.11.6.tgz#1fcbe61c2a6900c3539c06ee58901141f3558482" - integrity sha512-+w7BZCvkewSmaRM6H4L2QM3RL90teqEIHDIFXAmrW33+0jhlymnDAEdqVeCZATvxhQuio1ifoGVlJJbIiH9Ffg== +"@babel/cli@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.12.10.tgz#67a1015b1cd505bde1696196febf910c4c339a48" + integrity sha512-+y4ZnePpvWs1fc/LhZRTHkTesbXkyBYuOB+5CyodZqrEuETXi3zOVfpAQIdgC3lXbHLTDG9dQosxR9BhvLKDLQ== dependencies: commander "^4.0.1" convert-source-map "^1.1.0" @@ -16,7 +16,8 @@ slash "^2.0.0" source-map "^0.5.0" optionalDependencies: - chokidar "^2.1.8" + "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents" + chokidar "^3.4.0" "@babel/code-frame@7.8.3": version "7.8.3" @@ -32,6 +33,13 @@ dependencies: "@babel/highlight" "^7.10.4" +"@babel/code-frame@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + "@babel/compat-data@^7.10.4", "@babel/compat-data@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.11.0.tgz#e9f73efe09af1355b723a7f39b11bad637d7c99c" @@ -41,6 +49,16 @@ invariant "^2.2.4" semver "^5.5.0" +"@babel/compat-data@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.5.tgz#f56db0c4bb1bbbf221b4e81345aab4141e7cb0e9" + integrity sha512-DTsS7cxrsH3by8nqQSpFSyjSfSYl57D6Cf4q8dW3LK83tBKBDCkfcay1nYkXq1nIHXnpX8WMMb/O25HOy3h1zg== + +"@babel/compat-data@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.12.7.tgz#9329b4782a7d6bbd7eef57e11addf91ee3ef1e41" + integrity sha512-YaxPMGs/XIWtYqrdEOZOCPsVWfEoriXopnsz3/i7apYPXQ3698UFhS6dVT1KN5qOsWmVgw/FOrmQgpRaZayGsw== + "@babel/core@7.10.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.5.tgz#1f15e2cca8ad9a1d78a38ddba612f5e7cdbbd330" @@ -83,7 +101,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@>=7.9.0", "@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== @@ -105,6 +123,27 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.10.tgz#b79a2e1b9f70ed3d84bbfb6d8c4ef825f606bccd" + integrity sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.10" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.10" + "@babel/types" "^7.12.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.10.5", "@babel/generator@^7.11.5", "@babel/generator@^7.11.6", "@babel/generator@^7.4.4", "@babel/generator@^7.9.6": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" @@ -114,6 +153,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.12.10", "@babel/generator@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af" + integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA== + dependencies: + "@babel/types" "^7.12.11" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -121,6 +169,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-annotate-as-pure@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.10.tgz#54ab9b000e60a93644ce17b3f37d313aaf1d115d" + integrity sha512-XplmVbC1n+KY6jL8/fgLVXXUauDIB+lD5+GsQEh6F6GBF1dq1qy4DP4yXWzDKcoqXB3X58t61e85Fitoww4JVQ== + dependencies: + "@babel/types" "^7.12.10" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz#bb0b75f31bf98cbf9ff143c1ae578b87274ae1a3" @@ -157,6 +212,16 @@ levenary "^1.1.1" semver "^5.5.0" +"@babel/helper-compilation-targets@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831" + integrity sha512-+qH6NrscMolUlzOYngSBMIOQpKUGPPsc61Bu5W10mg84LxZ7cmvnBHzARKbDoFxVvqqAbj6Tg6N7bSrWSPXMyw== + dependencies: + "@babel/compat-data" "^7.12.5" + "@babel/helper-validator-option" "^7.12.1" + browserslist "^4.14.5" + semver "^5.5.0" + "@babel/helper-create-class-features-plugin@^7.10.4", "@babel/helper-create-class-features-plugin@^7.10.5", "@babel/helper-create-class-features-plugin@^7.3.0": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d" @@ -169,6 +234,17 @@ "@babel/helper-replace-supers" "^7.10.4" "@babel/helper-split-export-declaration" "^7.10.4" +"@babel/helper-create-class-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz#3c45998f431edd4a9214c5f1d3ad1448a6137f6e" + integrity sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + "@babel/helper-create-regexp-features-plugin@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz#fdd60d88524659a0b6959c0579925e425714f3b8" @@ -178,6 +254,15 @@ "@babel/helper-regex" "^7.10.4" regexpu-core "^4.7.0" +"@babel/helper-create-regexp-features-plugin@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz#18b1302d4677f9dc4740fe8c9ed96680e29d37e8" + integrity sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-regex" "^7.10.4" + regexpu-core "^4.7.1" + "@babel/helper-define-map@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz#b53c10db78a640800152692b13393147acb9bb30" @@ -204,6 +289,15 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-function-name@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42" + integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA== + dependencies: + "@babel/helper-get-function-arity" "^7.12.10" + "@babel/template" "^7.12.7" + "@babel/types" "^7.12.11" + "@babel/helper-get-function-arity@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" @@ -211,6 +305,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-get-function-arity@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz#b158817a3165b5faa2047825dfa61970ddcc16cf" + integrity sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag== + dependencies: + "@babel/types" "^7.12.10" + "@babel/helper-hoist-variables@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz#d49b001d1d5a68ca5e6604dda01a6297f7c9381e" @@ -225,6 +326,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-member-expression-to-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz#fba0f2fcff3fba00e6ecb664bb5e6e26e2d6165c" + integrity sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620" @@ -232,6 +340,13 @@ dependencies: "@babel/types" "^7.10.4" +"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz#1bfc0229f794988f76ed0a4d4e90860850b54dfb" + integrity sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA== + dependencies: + "@babel/types" "^7.12.5" + "@babel/helper-module-transforms@^7.10.4", "@babel/helper-module-transforms@^7.10.5", "@babel/helper-module-transforms@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359" @@ -245,6 +360,21 @@ "@babel/types" "^7.11.0" lodash "^4.17.19" +"@babel/helper-module-transforms@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz#7954fec71f5b32c48e4b303b437c34453fd7247c" + integrity sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-simple-access" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/helper-validator-identifier" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.1" + "@babel/types" "^7.12.1" + lodash "^4.17.19" + "@babel/helper-optimise-call-expression@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673" @@ -275,6 +405,15 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-remap-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz#8c4dbbf916314f6047dc05e6a2217074238347fd" + integrity sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-wrap-function" "^7.10.4" + "@babel/types" "^7.12.1" + "@babel/helper-replace-supers@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf" @@ -285,6 +424,16 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-replace-supers@^7.12.1": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.12.5.tgz#f009a17543bbbbce16b06206ae73b63d3fca68d9" + integrity sha512-5YILoed0ZyIpF4gKcpZitEnXEJ9UoDRki1Ey6xz46rxOzfNMAhVIJMoune1hmPVxh40LRv1+oafz7UsWX+vyWA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.12.1" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + "@babel/helper-simple-access@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461" @@ -293,6 +442,13 @@ "@babel/template" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helper-simple-access@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz#32427e5aa61547d38eb1e6eaf5fd1426fdad9136" + integrity sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-skip-transparent-expression-wrappers@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729" @@ -300,6 +456,13 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" + integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== + dependencies: + "@babel/types" "^7.12.1" + "@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" @@ -307,11 +470,33 @@ dependencies: "@babel/types" "^7.11.0" +"@babel/helper-split-export-declaration@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a" + integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g== + dependencies: + "@babel/types" "^7.12.11" + "@babel/helper-validator-identifier@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/helper-validator-option@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz#175567380c3e77d60ff98a54bb015fe78f2178d9" + integrity sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A== + +"@babel/helper-validator-option@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.11.tgz#d66cb8b7a3e7fe4c6962b32020a131ecf0847f4f" + integrity sha512-TBFCyj939mFSdeX7U7DDj32WtzYY7fDcalgq8v3fBZMNOJQNn7nOYzMaUCiPxPYfCup69mtIpqlKgMZLvQ8Xhw== + "@babel/helper-wrap-function@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz#8a6f701eab0ff39f765b5a1cfef409990e624b87" @@ -331,6 +516,15 @@ "@babel/traverse" "^7.10.4" "@babel/types" "^7.10.4" +"@babel/helpers@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.12.5.tgz#1a1ba4a768d9b58310eda516c449913fe647116e" + integrity sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA== + dependencies: + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.12.5" + "@babel/types" "^7.12.5" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" @@ -340,11 +534,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.11.2", "@babel/parser@^7.11.5", "@babel/parser@^7.2.0", "@babel/parser@^7.4.5", "@babel/parser@^7.9.6": +"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.11.5", "@babel/parser@^7.2.0", "@babel/parser@^7.4.5", "@babel/parser@^7.9.6": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== +"@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.7.0": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" + integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== + "@babel/plugin-proposal-async-generator-functions@^7.10.4", "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -354,6 +553,15 @@ "@babel/helper-remap-async-to-generator" "^7.10.4" "@babel/plugin-syntax-async-generators" "^7.8.0" +"@babel/plugin-proposal-async-generator-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e" + integrity sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-proposal-class-properties@7.3.0": version "7.3.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.0.tgz#272636bc0fa19a0bc46e601ec78136a173ea36cd" @@ -370,6 +578,14 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz#a082ff541f2a29a4821065b8add9346c0c16e5de" + integrity sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-decorators@^7.8.3": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz#42898bba478bc4b1ae242a703a953a7ad350ffb4" @@ -387,6 +603,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-dynamic-import" "^7.8.0" +"@babel/plugin-proposal-dynamic-import@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz#43eb5c2a3487ecd98c5c8ea8b5fdb69a2749b2dc" + integrity sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-proposal-export-default-from@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.10.4.tgz#08f66eef0067cbf6a7bc036977dcdccecaf0c6c5" @@ -403,6 +627,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-proposal-export-namespace-from@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz#8b9b8f376b2d88f5dd774e4d24a5cc2e3679b6d4" + integrity sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-proposal-json-strings@^7.10.4", "@babel/plugin-proposal-json-strings@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz#593e59c63528160233bd321b1aebe0820c2341db" @@ -411,6 +643,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.0" +"@babel/plugin-proposal-json-strings@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz#d45423b517714eedd5621a9dfdc03fa9f4eb241c" + integrity sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-proposal-logical-assignment-operators@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz#9f80e482c03083c87125dee10026b58527ea20c8" @@ -419,6 +659,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz#f2c490d36e1b3c9659241034a5d2cd50263a2751" + integrity sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a" @@ -427,6 +675,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz#3ed4fff31c015e7f3f1467f190dbe545cd7b046c" + integrity sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-proposal-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz#ce1590ff0a65ad12970a609d78855e9a4c1aef06" @@ -435,6 +691,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-proposal-numeric-separator@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.7.tgz#8bf253de8139099fea193b297d23a9d406ef056b" + integrity sha512-8c+uy0qmnRTeukiGsjLGy6uVs/TFjJchGXUeBqlG4VWYOdJWkhhVPdQ3uHwbmalfJwv2JsV0qffXP4asRfL2SQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread@7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.4.tgz#50129ac216b9a6a55b3853fdd923e74bf553a4c0" @@ -461,6 +725,15 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.0" "@babel/plugin-transform-parameters" "^7.10.4" +"@babel/plugin-proposal-object-rest-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding@^7.10.4", "@babel/plugin-proposal-optional-catch-binding@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz#31c938309d24a78a49d68fdabffaa863758554dd" @@ -469,6 +742,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" +"@babel/plugin-proposal-optional-catch-binding@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz#ccc2421af64d3aae50b558a71cede929a5ab2942" + integrity sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-proposal-optional-chaining@^7.10.1", "@babel/plugin-proposal-optional-chaining@^7.11.0": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076" @@ -478,6 +759,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" "@babel/plugin-syntax-optional-chaining" "^7.8.0" +"@babel/plugin-proposal-optional-chaining@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.7.tgz#e02f0ea1b5dc59d401ec16fb824679f683d3303c" + integrity sha512-4ovylXZ0PWmwoOvhU2vhnzVNnm88/Sm9nx7V8BPgMvAzn5zDou3/Awy0EjglyubVHasJj+XCEkr/r1X3P5elCA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-proposal-private-methods@^7.10.4", "@babel/plugin-proposal-private-methods@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz#b160d972b8fdba5c7d111a145fc8c421fc2a6909" @@ -486,6 +776,14 @@ "@babel/helper-create-class-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-private-methods@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz#86814f6e7a21374c980c10d38b4493e703f4a389" + integrity sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-proposal-unicode-property-regex@^7.10.4", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz#4483cda53041ce3413b7fe2f00022665ddfaa75d" @@ -494,6 +792,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-proposal-unicode-property-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz#2a183958d417765b9eae334f47758e5d6a82e072" + integrity sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -515,6 +821,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-class-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz#bcb297c5366e79bebadef509549cd93b04f19978" + integrity sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-decorators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz#6853085b2c429f9d322d02f5a635018cdeb2360c" @@ -571,6 +884,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-jsx@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -613,7 +933,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.10.4", "@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-top-level-await@^7.10.4", "@babel/plugin-syntax-top-level-await@^7.12.1", "@babel/plugin-syntax-top-level-await@^7.8.3": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz#dd6c0b357ac1bb142d98537450a319625d13d2a0" integrity sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A== @@ -627,6 +947,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.12.1.tgz#460ba9d77077653803c3dd2e673f76d66b4029e5" + integrity sha512-UZNEcCY+4Dp9yYRCAHrHDU+9ZXLYaY9MgBXSRLkB9WjYFRR6quJBumfVrEkUxrePPBwFcpWfNKXqVRQQtm7mMA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-arrow-functions@^7.10.4", "@babel/plugin-transform-arrow-functions@^7.2.0", "@babel/plugin-transform-arrow-functions@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz#e22960d77e697c74f41c501d44d73dbf8a6a64cd" @@ -634,6 +961,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-arrow-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz#8083ffc86ac8e777fbe24b5967c4b2521f3cb2b3" + integrity sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-async-to-generator@^7.10.4", "@babel/plugin-transform-async-to-generator@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz#41a5017e49eb6f3cda9392a51eef29405b245a37" @@ -643,6 +977,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-remap-async-to-generator" "^7.10.4" +"@babel/plugin-transform-async-to-generator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz#3849a49cc2a22e9743cbd6b52926d30337229af1" + integrity sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A== + dependencies: + "@babel/helper-module-imports" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-remap-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions@^7.10.4", "@babel/plugin-transform-block-scoped-functions@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz#1afa595744f75e43a91af73b0d998ecfe4ebc2e8" @@ -650,6 +993,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoped-functions@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz#f2a1a365bde2b7112e0a6ded9067fdd7c07905d9" + integrity sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-block-scoping@^7.10.4", "@babel/plugin-transform-block-scoping@^7.4.4", "@babel/plugin-transform-block-scoping@^7.8.3": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.11.1.tgz#5b7efe98852bef8d652c0b28144cd93a9e4b5215" @@ -657,6 +1007,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-block-scoping@^7.12.11": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca" + integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-classes@^7.10.4", "@babel/plugin-transform-classes@^7.4.4", "@babel/plugin-transform-classes@^7.9.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz#405136af2b3e218bc4a1926228bc917ab1a0adc7" @@ -671,6 +1028,20 @@ "@babel/helper-split-export-declaration" "^7.10.4" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz#65e650fcaddd3d88ddce67c0f834a3d436a32db6" + integrity sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-define-map" "^7.10.4" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-optimise-call-expression" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/helper-split-export-declaration" "^7.10.4" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.10.4", "@babel/plugin-transform-computed-properties@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz#9ded83a816e82ded28d52d4b4ecbdd810cdfc0eb" @@ -678,6 +1049,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-computed-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz#d68cf6c9b7f838a8a4144badbe97541ea0904852" + integrity sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-destructuring@^7.10.4", "@babel/plugin-transform-destructuring@^7.4.4", "@babel/plugin-transform-destructuring@^7.9.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz#70ddd2b3d1bea83d01509e9bb25ddb3a74fc85e5" @@ -685,6 +1063,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-destructuring@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz#b9a570fe0d0a8d460116413cb4f97e8e08b2f847" + integrity sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-dotall-regex@^7.10.4", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz#469c2062105c1eb6a040eaf4fac4b488078395ee" @@ -693,6 +1078,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-dotall-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz#a1d16c14862817b6409c0a678d6f9373ca9cd975" + integrity sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-duplicate-keys@^7.10.4", "@babel/plugin-transform-duplicate-keys@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz#697e50c9fee14380fe843d1f306b295617431e47" @@ -700,6 +1093,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-duplicate-keys@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz#745661baba295ac06e686822797a69fbaa2ca228" + integrity sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-exponentiation-operator@^7.10.4", "@babel/plugin-transform-exponentiation-operator@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz#5ae338c57f8cf4001bdb35607ae66b92d665af2e" @@ -708,6 +1108,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-exponentiation-operator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz#b0f2ed356ba1be1428ecaf128ff8a24f02830ae0" + integrity sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-flow-strip-types@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.4.tgz#c497957f09e86e3df7296271e9eb642876bf7788" @@ -723,6 +1131,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-for-of@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz#07640f28867ed16f9511c99c888291f560921cfa" + integrity sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-function-name@^7.10.4", "@babel/plugin-transform-function-name@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz#6a467880e0fc9638514ba369111811ddbe2644b7" @@ -731,6 +1146,14 @@ "@babel/helper-function-name" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-function-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz#2ec76258c70fe08c6d7da154003a480620eba667" + integrity sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw== + dependencies: + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-literals@^7.10.4", "@babel/plugin-transform-literals@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz#9f42ba0841100a135f22712d0e391c462f571f3c" @@ -738,6 +1161,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz#d73b803a26b37017ddf9d3bb8f4dc58bfb806f57" + integrity sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-member-expression-literals@^7.10.4", "@babel/plugin-transform-member-expression-literals@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz#b1ec44fcf195afcb8db2c62cd8e551c881baf8b7" @@ -745,6 +1175,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-member-expression-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz#496038602daf1514a64d43d8e17cbb2755e0c3ad" + integrity sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-modules-amd@^7.10.4", "@babel/plugin-transform-modules-amd@^7.2.0": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz#1b9cddaf05d9e88b3aad339cb3e445c4f020a9b1" @@ -754,6 +1191,15 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-amd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz#3154300b026185666eebb0c0ed7f8415fefcf6f9" + integrity sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-commonjs@^7.10.4", "@babel/plugin-transform-modules-commonjs@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz#66667c3eeda1ebf7896d41f1f16b17105a2fbca0" @@ -764,6 +1210,16 @@ "@babel/helper-simple-access" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz#fa403124542636c786cf9b460a0ffbb48a86e648" + integrity sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-simple-access" "^7.12.1" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-systemjs@^7.10.4", "@babel/plugin-transform-modules-systemjs@^7.4.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz#6270099c854066681bae9e05f87e1b9cadbe8c85" @@ -774,6 +1230,17 @@ "@babel/helper-plugin-utils" "^7.10.4" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz#663fea620d593c93f214a464cd399bf6dc683086" + integrity sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q== + dependencies: + "@babel/helper-hoist-variables" "^7.10.4" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-identifier" "^7.10.4" + babel-plugin-dynamic-import-node "^2.3.3" + "@babel/plugin-transform-modules-umd@^7.10.4", "@babel/plugin-transform-modules-umd@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz#9a8481fe81b824654b3a0b65da3df89f3d21839e" @@ -782,6 +1249,14 @@ "@babel/helper-module-transforms" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-modules-umd@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz#eb5a218d6b1c68f3d6217b8fa2cc82fec6547902" + integrity sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q== + dependencies: + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-named-capturing-groups-regex@^7.10.4", "@babel/plugin-transform-named-capturing-groups-regex@^7.4.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz#78b4d978810b6f3bcf03f9e318f2fc0ed41aecb6" @@ -789,6 +1264,13 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.10.4" +"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz#b407f5c96be0d9f5f88467497fa82b30ac3e8753" + integrity sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/plugin-transform-new-target@^7.10.4", "@babel/plugin-transform-new-target@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz#9097d753cb7b024cb7381a3b2e52e9513a9c6888" @@ -796,6 +1278,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-new-target@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz#80073f02ee1bb2d365c3416490e085c95759dec0" + integrity sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-object-super@^7.10.4", "@babel/plugin-transform-object-super@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz#d7146c4d139433e7a6526f888c667e314a093894" @@ -804,6 +1293,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-replace-supers" "^7.10.4" +"@babel/plugin-transform-object-super@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz#4ea08696b8d2e65841d0c7706482b048bed1066e" + integrity sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-replace-supers" "^7.12.1" + "@babel/plugin-transform-parameters@^7.10.4", "@babel/plugin-transform-parameters@^7.4.4", "@babel/plugin-transform-parameters@^7.9.5": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz#59d339d58d0b1950435f4043e74e2510005e2c4a" @@ -812,6 +1309,13 @@ "@babel/helper-get-function-arity" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-parameters@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz#d2e963b038771650c922eff593799c96d853255d" + integrity sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-property-literals@^7.10.4", "@babel/plugin-transform-property-literals@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz#f6fe54b6590352298785b83edd815d214c42e3c0" @@ -819,6 +1323,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-property-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz#41bc81200d730abb4456ab8b3fbd5537b59adecd" + integrity sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-constant-elements@^7.9.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.10.4.tgz#0f485260bf1c29012bb973e7e404749eaac12c9e" @@ -833,6 +1344,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-react-display-name@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.12.1.tgz#1cbcd0c3b1d6648c55374a22fc9b6b7e5341c00d" + integrity sha512-cAzB+UzBIrekfYxyLlFqf/OagTvHLcVBb5vpouzkYkBclRPraiygVnafvAoipErZLI8ANv8Ecn6E/m5qPXD26w== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-jsx-development@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.4.tgz#6ec90f244394604623880e15ebc3c34c356258ba" @@ -842,6 +1360,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx-development@^7.12.7": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.12.tgz#bccca33108fe99d95d7f9e82046bfe762e71f4e7" + integrity sha512-i1AxnKxHeMxUaWVXQOSIco4tvVvvCxMSfeBMnMM06mpaJt3g+MpxYQQrDfojUQldP1xxraPSJYSMEljoWM/dCg== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.12" + "@babel/plugin-transform-react-jsx-self@^7.0.0", "@babel/plugin-transform-react-jsx-self@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.4.tgz#cd301a5fed8988c182ed0b9d55e9bd6db0bd9369" @@ -868,6 +1393,17 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-jsx" "^7.10.4" +"@babel/plugin-transform-react-jsx@^7.12.10", "@babel/plugin-transform-react-jsx@^7.12.12": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.12.tgz#b0da51ffe5f34b9a900e9f1f5fb814f9e512d25e" + integrity sha512-JDWGuzGNWscYcq8oJVCtSE61a5+XAOos+V0HrxnDieUus4UMnBEosDnY1VJqU5iZ4pA04QY7l0+JvHL1hZEfsw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.12.10" + "@babel/helper-module-imports" "^7.12.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-jsx" "^7.12.1" + "@babel/types" "^7.12.12" + "@babel/plugin-transform-react-pure-annotations@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.4.tgz#3eefbb73db94afbc075f097523e445354a1c6501" @@ -876,6 +1412,14 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-react-pure-annotations@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42" + integrity sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.10.4" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-regenerator@^7.10.4", "@babel/plugin-transform-regenerator@^7.4.5": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz#2015e59d839074e76838de2159db421966fd8b63" @@ -883,6 +1427,13 @@ dependencies: regenerator-transform "^0.14.2" +"@babel/plugin-transform-regenerator@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz#5f0a28d842f6462281f06a964e88ba8d7ab49753" + integrity sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng== + dependencies: + regenerator-transform "^0.14.2" + "@babel/plugin-transform-reserved-words@^7.10.4", "@babel/plugin-transform-reserved-words@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz#8f2682bcdcef9ed327e1b0861585d7013f8a54dd" @@ -890,6 +1441,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-reserved-words@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz#6fdfc8cc7edcc42b36a7c12188c6787c873adcd8" + integrity sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-runtime@7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.2.0.tgz#566bc43f7d0aedc880eaddbd29168d0f248966ea" @@ -900,14 +1458,13 @@ resolve "^1.8.1" semver "^5.5.1" -"@babel/plugin-transform-runtime@^7.11.0": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz#e27f78eb36f19448636e05c33c90fd9ad9b8bccf" - integrity sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw== +"@babel/plugin-transform-runtime@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.10.tgz#af0fded4e846c4b37078e8e5d06deac6cd848562" + integrity sha512-xOrUfzPxw7+WDm9igMgQCbO3cJKymX7dFdsgRr1eu9n3KjjyU4pptIXbXPseQDquw+W+RuJEJMHKHNsPNNm3CA== dependencies: - "@babel/helper-module-imports" "^7.10.4" + "@babel/helper-module-imports" "^7.12.5" "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" semver "^5.5.1" "@babel/plugin-transform-shorthand-properties@^7.10.4", "@babel/plugin-transform-shorthand-properties@^7.2.0", "@babel/plugin-transform-shorthand-properties@^7.8.3": @@ -917,6 +1474,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-shorthand-properties@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz#0bf9cac5550fce0cfdf043420f661d645fdc75e3" + integrity sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-spread@^7.11.0", "@babel/plugin-transform-spread@^7.2.0", "@babel/plugin-transform-spread@^7.8.3": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz#fa84d300f5e4f57752fe41a6d1b3c554f13f17cc" @@ -925,6 +1489,14 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0" +"@babel/plugin-transform-spread@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz#527f9f311be4ec7fdc2b79bb89f7bf884b3e1e1e" + integrity sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" + "@babel/plugin-transform-sticky-regex@^7.10.4", "@babel/plugin-transform-sticky-regex@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz#8f3889ee8657581130a29d9cc91d7c73b7c4a28d" @@ -933,6 +1505,13 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/helper-regex" "^7.10.4" +"@babel/plugin-transform-sticky-regex@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.7.tgz#560224613ab23987453948ed21d0b0b193fa7fad" + integrity sha512-VEiqZL5N/QvDbdjfYQBhruN0HYjSPjC4XkeqW4ny/jNtH9gcbgaqBIXYEZCNnESMAGs0/K/R7oFGMhOyu/eIxg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-template-literals@^7.10.4", "@babel/plugin-transform-template-literals@^7.4.4", "@babel/plugin-transform-template-literals@^7.8.3": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz#78bc5d626a6642db3312d9d0f001f5e7639fde8c" @@ -941,6 +1520,13 @@ "@babel/helper-annotate-as-pure" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-template-literals@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz#b43ece6ed9a79c0c71119f576d299ef09d942843" + integrity sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typeof-symbol@^7.10.4", "@babel/plugin-transform-typeof-symbol@^7.2.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz#9509f1a7eec31c4edbffe137c16cc33ff0bc5bfc" @@ -948,6 +1534,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-typeof-symbol@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.10.tgz#de01c4c8f96580bd00f183072b0d0ecdcf0dec4b" + integrity sha512-JQ6H8Rnsogh//ijxspCjc21YPd3VLVoYtAwv3zQmqAt8YGYUtdo5usNhdl4b9/Vir2kPFZl6n1h0PfUz4hJhaA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-typescript@^7.10.4": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.11.0.tgz#2b4879676af37342ebb278216dd090ac67f13abb" @@ -957,6 +1550,15 @@ "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-typescript" "^7.10.4" +"@babel/plugin-transform-typescript@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.12.1.tgz#d92cc0af504d510e26a754a7dbc2e5c8cd9c7ab4" + integrity sha512-VrsBByqAIntM+EYMqSm59SiMEf7qkmI9dqMt6RbD/wlwueWmYcI0FFK5Fj47pP6DRZm+3teXjosKlwcZJ5lIMw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-typescript" "^7.12.1" + "@babel/plugin-transform-unicode-escapes@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz#feae523391c7651ddac115dae0a9d06857892007" @@ -964,6 +1566,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-escapes@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz#5232b9f81ccb07070b7c3c36c67a1b78f1845709" + integrity sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-unicode-regex@^7.10.4", "@babel/plugin-transform-unicode-regex@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz#e56d71f9282fac6db09c82742055576d5e6d80a8" @@ -972,6 +1581,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-transform-unicode-regex@^7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz#cc9661f61390db5c65e3febaccefd5c6ac3faecb" + integrity sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.12.1" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/preset-env@7.4.5": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.4.5.tgz#2fad7f62983d5af563b5f3139242755884998a58" @@ -1026,7 +1643,79 @@ js-levenshtein "^1.1.3" semver "^5.5.0" -"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.9.5", "@babel/preset-env@^7.9.6": +"@babel/preset-env@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.11.tgz#55d5f7981487365c93dbbc84507b1c7215e857f9" + integrity sha512-j8Tb+KKIXKYlDBQyIOy4BLxzv1NUOwlHfZ74rvW+Z0Gp4/cI2IMDPBWAgWceGcE7aep9oL/0K9mlzlMGxA8yNw== + dependencies: + "@babel/compat-data" "^7.12.7" + "@babel/helper-compilation-targets" "^7.12.5" + "@babel/helper-module-imports" "^7.12.5" + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.11" + "@babel/plugin-proposal-async-generator-functions" "^7.12.1" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-dynamic-import" "^7.12.1" + "@babel/plugin-proposal-export-namespace-from" "^7.12.1" + "@babel/plugin-proposal-json-strings" "^7.12.1" + "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-numeric-separator" "^7.12.7" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-class-properties" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.12.1" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-async-to-generator" "^7.12.1" + "@babel/plugin-transform-block-scoped-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.11" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-computed-properties" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-dotall-regex" "^7.12.1" + "@babel/plugin-transform-duplicate-keys" "^7.12.1" + "@babel/plugin-transform-exponentiation-operator" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-function-name" "^7.12.1" + "@babel/plugin-transform-literals" "^7.12.1" + "@babel/plugin-transform-member-expression-literals" "^7.12.1" + "@babel/plugin-transform-modules-amd" "^7.12.1" + "@babel/plugin-transform-modules-commonjs" "^7.12.1" + "@babel/plugin-transform-modules-systemjs" "^7.12.1" + "@babel/plugin-transform-modules-umd" "^7.12.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" + "@babel/plugin-transform-new-target" "^7.12.1" + "@babel/plugin-transform-object-super" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-property-literals" "^7.12.1" + "@babel/plugin-transform-regenerator" "^7.12.1" + "@babel/plugin-transform-reserved-words" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/plugin-transform-sticky-regex" "^7.12.7" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/plugin-transform-typeof-symbol" "^7.12.10" + "@babel/plugin-transform-unicode-escapes" "^7.12.1" + "@babel/plugin-transform-unicode-regex" "^7.12.1" + "@babel/preset-modules" "^0.1.3" + "@babel/types" "^7.12.11" + core-js-compat "^3.8.0" + semver "^5.5.0" + +"@babel/preset-env@^7.9.5", "@babel/preset-env@^7.9.6": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== @@ -1130,7 +1819,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.10.4", "@babel/preset-react@^7.8.3", "@babel/preset-react@^7.9.4": +"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.8.3", "@babel/preset-react@^7.9.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.10.4.tgz#92e8a66d816f9911d11d4cc935be67adfc82dbcf" integrity sha512-BrHp4TgOIy4M19JAfO1LhycVXOPWdDbTRep7eVyatf174Hff+6Uk53sDyajqZPu8W1qXRBiYOfIamek6jA7YVw== @@ -1143,7 +1832,27 @@ "@babel/plugin-transform-react-jsx-source" "^7.10.4" "@babel/plugin-transform-react-pure-annotations" "^7.10.4" -"@babel/preset-typescript@^7.10.4", "@babel/preset-typescript@^7.9.0": +"@babel/preset-react@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.10.tgz#4fed65f296cbb0f5fb09de6be8cddc85cc909be9" + integrity sha512-vtQNjaHRl4DUpp+t+g4wvTHsLQuye+n0H/wsXIZRn69oz/fvNC7gQ4IK73zGJBaxvHoxElDvnYCthMcT7uzFoQ== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-transform-react-display-name" "^7.12.1" + "@babel/plugin-transform-react-jsx" "^7.12.10" + "@babel/plugin-transform-react-jsx-development" "^7.12.7" + "@babel/plugin-transform-react-pure-annotations" "^7.12.1" + +"@babel/preset-typescript@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.12.7.tgz#fc7df8199d6aae747896f1e6c61fc872056632a3" + integrity sha512-nOoIqIqBmHBSEgBXWR4Dv/XBehtIFcw9PqZw6rFYuKrzsZmOQm3PR5siLBnKZFEsDb03IegG8nSjU/iXXXYRmw== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/helper-validator-option" "^7.12.1" + "@babel/plugin-transform-typescript" "^7.12.1" + +"@babel/preset-typescript@^7.9.0": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz#7d5d052e52a682480d6e2cc5aa31be61c8c25e36" integrity sha512-SdYnvGPv+bLlwkF2VkJnaX/ni1sMNetcGI1+nThF1gyv6Ph8Qucc4ZZAjM5yZcE/AKRXIOTZz7eSRDWOEjPyRQ== @@ -1162,6 +1871,17 @@ pirates "^4.0.0" source-map-support "^0.5.16" +"@babel/register@^7.12.10": + version "7.12.10" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.12.10.tgz#19b87143f17128af4dbe7af54c735663b3999f60" + integrity sha512-EvX/BvMMJRAA3jZgILWgbsrHwBQvllC5T8B29McyME8DvkdOxk4ujESfrMvME8IHSDvWXrmMXxPvA/lx2gqPLQ== + dependencies: + find-cache-dir "^2.0.0" + lodash "^4.17.19" + make-dir "^2.1.0" + pirates "^4.0.0" + source-map-support "^0.5.16" + "@babel/runtime-corejs2@^7.2.0": version "7.11.2" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.11.2.tgz#700a03945ebad0d31ba6690fc8a6bcc9040faa47" @@ -1199,6 +1919,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" + integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.3.3", "@babel/template@^7.4.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -1208,7 +1935,16 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5", "@babel/traverse@^7.11.5", "@babel/traverse@^7.4.5": +"@babel/template@^7.12.7": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5", "@babel/traverse@^7.11.5", "@babel/traverse@^7.4.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== @@ -1223,6 +1959,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.12", "@babel/traverse@^7.12.5", "@babel/traverse@^7.7.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376" + integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== + dependencies: + "@babel/code-frame" "^7.12.11" + "@babel/generator" "^7.12.11" + "@babel/helper-function-name" "^7.12.11" + "@babel/helper-split-export-declaration" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/types" "^7.12.12" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" @@ -1232,6 +1983,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.7.0": + version "7.12.12" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" + integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -3061,6 +3821,23 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents": + version "2.1.8-no-fsevents" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz#da7c3996b8e6e19ebd14d82eaced2313e7769f9b" + integrity sha512-+nb9vWloHNNMFHjGofEam3wopE3m1yuambrrd/fnPc+lFOMB9ROTqQlche9ByFWNkdNqfSgR/kkQtQ8DzEWt2w== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + "@nodelib/fs.scandir@2.1.3": version "2.1.3" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" @@ -4555,7 +5332,7 @@ resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.10", "@types/babel__core@^7.1.7": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.10" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== @@ -4566,6 +5343,17 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" +"@types/babel__core@^7.1.12": + version "7.1.12" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d" + integrity sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__generator@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" @@ -7492,6 +8280,11 @@ async-done@^1.2.0, async-done@^1.2.2: process-nextick-args "^2.0.0" stream-exhaust "^1.0.1" +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" @@ -7675,15 +8468,15 @@ babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-eslint@^10.0.3: - version "10.0.3" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" - integrity sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA== +babel-eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" resolve "^1.12.0" @@ -7736,7 +8529,7 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^26.3.0, babel-jest@^26.6.3: +babel-jest@^26.6.3: version "26.6.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== @@ -7760,6 +8553,16 @@ babel-loader@^8.0.6: mkdirp "^0.5.1" pify "^4.0.1" +babel-loader@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" + integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^1.4.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + babel-messages@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" @@ -7767,13 +8570,18 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-add-module-exports@1.0.2, babel-plugin-add-module-exports@^1.0.2: +babel-plugin-add-module-exports@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-1.0.2.tgz#96cd610d089af664f016467fc4567c099cce2d9c" integrity sha512-4paN7RivvU3Rzju1vGSHWPjO8Y0rI6droWvSFKI6dvEQ4mvoV0zGojnlzVRfI6N8zISo6VERXt3coIuVmzuvNg== optionalDependencies: chokidar "^2.0.4" +babel-plugin-add-module-exports@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-1.0.4.tgz#6caa4ddbe1f578c6a5264d4d3e6c8a2720a7ca2b" + integrity sha512-g+8yxHUZ60RcyaUpfNzy56OtWW+x9cyEe9j+CranqLiqbju2yf/Cy6ZtYK40EZxtrdHllzlVZgLmcOUCTlJ7Jg== + babel-plugin-add-react-displayname@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" @@ -8304,6 +9112,11 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + binary-extensions@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" @@ -8450,7 +9263,7 @@ brace@0.11.1, brace@^0.11.0, brace@^0.11.1: resolved "https://registry.yarnpkg.com/brace/-/brace-0.11.1.tgz#4896fcc9d544eef45f4bb7660db320d3b379fe58" integrity sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg= -braces@^2.3.1: +braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== @@ -8746,6 +9559,28 @@ browserslist@^4.12.0, browserslist@^4.8.3: node-releases "^1.1.53" pkg-up "^2.0.0" +browserslist@^4.14.5: + version "4.14.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.7.tgz#c071c1b3622c1c2e790799a37bb09473a4351cb6" + integrity sha512-BSVRLCeG3Xt/j/1cCGj1019Wbty0H+Yvu2AOuZSuoaUWn3RatbL33Cxk+Q4jRMRAbOm0p7SLravLjpnT6s0vzQ== + dependencies: + caniuse-lite "^1.0.30001157" + colorette "^1.2.1" + electron-to-chromium "^1.3.591" + escalade "^3.1.1" + node-releases "^1.1.66" + +browserslist@^4.16.1: + version "4.16.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.1.tgz#bf757a2da376b3447b800a16f0f1c96358138766" + integrity sha512-UXhDrwqsNcpTYJBTZsbGATDxZbiVDsx6UjpmRUmtnP10pr8wAYr5LgFoEFw9ixriQH2mv/NX2SfGzE/o8GndLA== + dependencies: + caniuse-lite "^1.0.30001173" + colorette "^1.2.1" + electron-to-chromium "^1.3.634" + escalade "^3.1.1" + node-releases "^1.1.69" + browserslist@^4.6.0, browserslist@^4.8.5: version "4.14.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.5.tgz#1c751461a102ddc60e40993639b709be7f2c4015" @@ -9100,11 +9935,16 @@ camelize@^1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= -caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135: +caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001097, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001135, caniuse-lite@^1.0.30001173: version "1.0.30001179" resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001179.tgz" integrity sha512-blMmO0QQujuUWZKyVrD1msR4WNDAqb/UPO1Sw2WWsQ7deoM5bJiicKnWJ1Y0NS/aGINSnKPIWBMw5luX+NDUCA== +caniuse-lite@^1.0.30001157: + version "1.0.30001164" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001164.tgz#5bbfd64ca605d43132f13cc7fdabb17c3036bfdc" + integrity sha512-G+A/tkf4bu0dSp9+duNiXc7bGds35DioCyC6vgK2m/rjA4Krpy5WeZgZyfH2f0wj2kI6yAWWucyap6oOwmY1mg== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -9343,7 +10183,7 @@ cheerio@^1.0.0-rc.3: lodash "^4.15.0" parse5 "^3.0.1" -chokidar@2.1.2, chokidar@3.3.0, chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.1, chokidar@^2.1.2, chokidar@^2.1.8, chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.4.1, chokidar@^3.4.3: +chokidar@2.1.2, chokidar@3.3.0, chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.1, chokidar@^2.1.2, chokidar@^2.1.8, chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== @@ -10226,6 +11066,14 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" +core-js-compat@^3.8.0: + version "3.8.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.8.3.tgz#9123fb6b9cad30f0651332dc77deba48ef9b0b3f" + integrity sha512-1sCb0wBXnBIL16pfFG1Gkvei6UzvKyTNYpiC41yrdjEv0UoJoq9E/abTMzyYJ6JpTkAj15dLjbqifIzEBDVvog== + dependencies: + browserslist "^4.16.1" + semver "7.0.0" + core-js-pure@^3.0.0, core-js-pure@^3.0.1: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" @@ -12191,6 +13039,16 @@ electron-to-chromium@^1.3.571: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.577.tgz#9885f3f72c6e3367010b461ff6f2d9624a929720" integrity sha512-dSb64JQSFif/pD8mpVAgSFkbVi6YHbK6JeEziwNNmXlr/Ne2rZtseFK5SM7JoWSLf6gP0gVvRGi4/2ZRhSX/rA== +electron-to-chromium@^1.3.591: + version "1.3.598" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.598.tgz#8f757018902ab6190323a8c5f6124d854893a35b" + integrity sha512-G5Ztk23/ubLYVPxPXnB1uu105uzIPd4xB/D8ld8x1GaSC9+vU9NZL16nYZya8H77/7CCKKN7dArzJL3pBs8N7A== + +electron-to-chromium@^1.3.634: + version "1.3.642" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.642.tgz#8b884f50296c2ae2a9997f024d0e3e57facc2b94" + integrity sha512-cev+jOrz/Zm1i+Yh334Hed6lQVOkkemk2wRozfMF4MtTR7pxf3r3L5Rbd7uX1zMcEqVJ7alJBnJL7+JffkC6FQ== + elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" @@ -12740,10 +13598,10 @@ eslint-module-utils@2.5.0, eslint-module-utils@^2.4.1: debug "^2.6.9" pkg-dir "^2.0.0" -eslint-plugin-babel@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.3.0.tgz#2e7f251ccc249326da760c1a4c948a91c32d0023" - integrity sha512-HPuNzSPE75O+SnxHIafbW5QB45r2w78fxqwK3HmjqIUoPfPzVrq6rD+CINU3yzoDSzEhUkX07VUphbF73Lth/w== +eslint-plugin-babel@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz#75a2413ffbf17e7be57458301c60291f2cfbf560" + integrity sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g== dependencies: eslint-rule-composer "^0.3.0" @@ -16486,6 +17344,13 @@ is-bigint@^1.0.0: resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -20656,6 +21521,16 @@ node-releases@^1.1.61: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.61.tgz#707b0fca9ce4e11783612ba4a2fcba09047af16e" integrity sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g== +node-releases@^1.1.66: + version "1.1.67" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.67.tgz#28ebfcccd0baa6aad8e8d4d8fe4cbc49ae239c12" + integrity sha512-V5QF9noGFl3EymEwUYzO+3NTDpGfQB4ve6Qfnzf3UNydMhjQRVPR1DZTuvWiLzaFJYw2fmDwAfnRNEVb64hSIg== + +node-releases@^1.1.69: + version "1.1.70" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.70.tgz#66e0ed0273aa65666d7fe78febe7634875426a08" + integrity sha512-Slf2s69+2/uAD79pVVQo8uSiC34+g8GWY8UH2Qtqv34ZfhYrxpYpfzs9Js9d6O0mbDmALuxaTlplnBTnSELcrw== + node-sass@^4.14.1: version "4.14.1" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" @@ -23847,6 +24722,15 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" @@ -24102,6 +24986,18 @@ regexpu-core@^4.7.0: unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.2.0" +regexpu-core@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" + integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.2.0" + regjsgen "^0.5.1" + regjsparser "^0.6.4" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.2.0" + registry-auth-token@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479" @@ -28175,6 +29071,11 @@ unzip-response@^1.0.0: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" integrity sha1-uYTwh3/AqJwsdzzB73tbIytbBv4= +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + update-notifier@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc" From 0076384a0f9de9d35711008eaa8b244a2d6a487c Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Tue, 26 Jan 2021 12:32:12 -0500 Subject: [PATCH 57/90] [Maps] Implement fields and bounds retrieval on GeoJsonFileSource (#88294) --- .../source_descriptor_types.ts | 6 + .../classes/fields/geojson_file_field.ts | 43 +++++++ .../layers/file_upload_wizard/wizard.tsx | 7 +- .../geojson_file_source/geojson_file.test.ts | 105 ++++++++++++++++++ .../geojson_file_source.ts | 88 ++++++++++++--- .../sources/geojson_file_source/index.ts | 2 +- .../maps/public/selectors/map_selectors.ts | 12 +- 7 files changed, 240 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/maps/public/classes/fields/geojson_file_field.ts create mode 100644 x-pack/plugins/maps/public/classes/sources/geojson_file_source/geojson_file.test.ts diff --git a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts index 603e1d767e1c6..b849b42429cf6 100644 --- a/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/source_descriptor_types.ts @@ -156,7 +156,13 @@ export type TiledSingleLayerVectorSourceDescriptor = AbstractSourceDescriptor & tooltipProperties: string[]; }; +export type GeoJsonFileFieldDescriptor = { + name: string; + type: 'string' | 'number'; +}; + export type GeojsonFileSourceDescriptor = { + __fields?: GeoJsonFileFieldDescriptor[]; __featureCollection: FeatureCollection; name: string; type: string; diff --git a/x-pack/plugins/maps/public/classes/fields/geojson_file_field.ts b/x-pack/plugins/maps/public/classes/fields/geojson_file_field.ts new file mode 100644 index 0000000000000..ae42b09d491c5 --- /dev/null +++ b/x-pack/plugins/maps/public/classes/fields/geojson_file_field.ts @@ -0,0 +1,43 @@ +/* + * 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 { FIELD_ORIGIN } from '../../../common/constants'; +import { IField, AbstractField } from './field'; +import { IVectorSource } from '../sources/vector_source'; +import { GeoJsonFileSource } from '../sources/geojson_file_source'; + +export class GeoJsonFileField extends AbstractField implements IField { + private readonly _source: GeoJsonFileSource; + private readonly _dataType: string; + + constructor({ + fieldName, + source, + origin, + dataType, + }: { + fieldName: string; + source: GeoJsonFileSource; + origin: FIELD_ORIGIN; + dataType: string; + }) { + super({ fieldName, origin }); + this._source = source; + this._dataType = dataType; + } + + getSource(): IVectorSource { + return this._source; + } + + async getLabel(): Promise { + return this.getName(); + } + + async getDataType(): Promise { + return this._dataType; + } +} diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index a0029c5c64e0b..68fd25ce9e7ae 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -14,7 +14,7 @@ import { SCALING_TYPES, } from '../../../../common/constants'; import { getFileUploadComponent } from '../../../kibana_services'; -import { GeojsonFileSource } from '../../sources/geojson_file_source'; +import { GeoJsonFileSource } from '../../sources/geojson_file_source'; import { VectorLayer } from '../../layers/vector_layer/vector_layer'; import { createDefaultLayerDescriptor } from '../../sources/es_search_source'; import { RenderWizardArguments } from '../../layers/layer_wizard_registry'; @@ -79,7 +79,10 @@ export class ClientFileCreateSourceEditor extends Component { + describe('getName', () => { + it('should get default display name', async () => { + const geojsonFileSource = new GeoJsonFileSource({}); + expect(await geojsonFileSource.getDisplayName()).toBe('Features'); + }); + }); + describe('getBounds', () => { + it('should get null bounds', async () => { + const geojsonFileSource = new GeoJsonFileSource({}); + expect( + await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {}) + ).toEqual(null); + }); + + it('should get bounds from feature collection', async () => { + const geojsonFileSource = new GeoJsonFileSource({ + __featureCollection: { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [0, 1], + }, + properties: {}, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [2, 3], + }, + properties: {}, + }, + ], + }, + }); + + expect(geojsonFileSource.isBoundsAware()).toBe(true); + expect( + await geojsonFileSource.getBoundsForFilters(({} as unknown) as BoundsFilters, () => {}) + ).toEqual({ + maxLat: 3, + maxLon: 2, + minLat: 1, + minLon: 0, + }); + }); + }); + + describe('getFields', () => { + it('should get fields from config', async () => { + const geojsonFileSource = new GeoJsonFileSource({ + __fields: [ + { + type: 'string', + name: 'foo', + }, + { + type: 'number', + name: 'bar', + }, + ], + }); + + const fields = await geojsonFileSource.getFields(); + + const actualFields = fields.map(async (field) => { + return { + dataType: await field.getDataType(), + origin: field.getOrigin(), + name: field.getName(), + source: field.getSource(), + }; + }); + + expect(await Promise.all(actualFields)).toEqual([ + { + dataType: 'string', + origin: FIELD_ORIGIN.SOURCE, + source: geojsonFileSource, + name: 'foo', + }, + { + dataType: 'number', + origin: FIELD_ORIGIN.SOURCE, + source: geojsonFileSource, + name: 'bar', + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/classes/sources/geojson_file_source/geojson_file_source.ts b/x-pack/plugins/maps/public/classes/sources/geojson_file_source/geojson_file_source.ts index 6172405152739..69d84dc65d382 100644 --- a/x-pack/plugins/maps/public/classes/sources/geojson_file_source/geojson_file_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/geojson_file_source/geojson_file_source.ts @@ -5,13 +5,22 @@ */ import { Feature, FeatureCollection } from 'geojson'; -import { AbstractVectorSource, GeoJsonWithMeta } from '../vector_source'; -import { EMPTY_FEATURE_COLLECTION, SOURCE_TYPES } from '../../../../common/constants'; -import { GeojsonFileSourceDescriptor } from '../../../../common/descriptor_types'; +import { AbstractVectorSource, BoundsFilters, GeoJsonWithMeta } from '../vector_source'; +import { EMPTY_FEATURE_COLLECTION, FIELD_ORIGIN, SOURCE_TYPES } from '../../../../common/constants'; +import { + GeoJsonFileFieldDescriptor, + GeojsonFileSourceDescriptor, + MapExtent, +} from '../../../../common/descriptor_types'; import { registerSource } from '../source_registry'; import { IField } from '../../fields/field'; +import { getFeatureCollectionBounds } from '../../util/get_feature_collection_bounds'; +import { GeoJsonFileField } from '../../fields/geojson_file_field'; +import { Adapters } from '../../../../../../../src/plugins/inspector/common/adapters'; -function getFeatureCollection(geoJson: Feature | FeatureCollection | null): FeatureCollection { +function getFeatureCollection( + geoJson: Feature | FeatureCollection | null | undefined +): FeatureCollection { if (!geoJson) { return EMPTY_FEATURE_COLLECTION; } @@ -30,18 +39,73 @@ function getFeatureCollection(geoJson: Feature | FeatureCollection | null): Feat return EMPTY_FEATURE_COLLECTION; } -export class GeojsonFileSource extends AbstractVectorSource { +export class GeoJsonFileSource extends AbstractVectorSource { static createDescriptor( - geoJson: Feature | FeatureCollection | null, - name: string + descriptor: Partial ): GeojsonFileSourceDescriptor { return { type: SOURCE_TYPES.GEOJSON_FILE, - __featureCollection: getFeatureCollection(geoJson), - name, + __featureCollection: getFeatureCollection(descriptor.__featureCollection), + __fields: descriptor.__fields || [], + name: descriptor.name || 'Features', }; } + constructor(descriptor: Partial, inspectorAdapters?: Adapters) { + const normalizedDescriptor = GeoJsonFileSource.createDescriptor(descriptor); + super(normalizedDescriptor, inspectorAdapters); + } + + _getFields(): GeoJsonFileFieldDescriptor[] { + const fields = (this._descriptor as GeojsonFileSourceDescriptor).__fields; + return fields ? fields : []; + } + + createField({ fieldName }: { fieldName: string }): IField { + const fields = this._getFields(); + const descriptor: GeoJsonFileFieldDescriptor | undefined = fields.find((field) => { + return field.name === fieldName; + }); + + if (!descriptor) { + throw new Error( + `Cannot find corresponding field ${fieldName} in __fields array ${JSON.stringify( + this._getFields() + )} ` + ); + } + return new GeoJsonFileField({ + fieldName: descriptor.name, + source: this, + origin: FIELD_ORIGIN.SOURCE, + dataType: descriptor.type, + }); + } + + async getFields(): Promise { + const fields = this._getFields(); + return fields.map((field: GeoJsonFileFieldDescriptor) => { + return new GeoJsonFileField({ + fieldName: field.name, + source: this, + origin: FIELD_ORIGIN.SOURCE, + dataType: field.type, + }); + }); + } + + isBoundsAware(): boolean { + return true; + } + + async getBoundsForFilters( + boundsFilters: BoundsFilters, + registerCancelCallback: (callback: () => void) => void + ): Promise { + const featureCollection = (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection; + return getFeatureCollectionBounds(featureCollection, false); + } + async getGeoJsonWithMeta(): Promise { return { data: (this._descriptor as GeojsonFileSourceDescriptor).__featureCollection, @@ -49,10 +113,6 @@ export class GeojsonFileSource extends AbstractVectorSource { }; } - createField({ fieldName }: { fieldName: string }): IField { - throw new Error('Not implemented'); - } - async getDisplayName() { return (this._descriptor as GeojsonFileSourceDescriptor).name; } @@ -63,6 +123,6 @@ export class GeojsonFileSource extends AbstractVectorSource { } registerSource({ - ConstructorFunction: GeojsonFileSource, + ConstructorFunction: GeoJsonFileSource, type: SOURCE_TYPES.GEOJSON_FILE, }); diff --git a/x-pack/plugins/maps/public/classes/sources/geojson_file_source/index.ts b/x-pack/plugins/maps/public/classes/sources/geojson_file_source/index.ts index cf0d15dcb747a..ef6806e0aa151 100644 --- a/x-pack/plugins/maps/public/classes/sources/geojson_file_source/index.ts +++ b/x-pack/plugins/maps/public/classes/sources/geojson_file_source/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { GeojsonFileSource } from './geojson_file_source'; +export { GeoJsonFileSource } from './geojson_file_source'; diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index 7b72a3c979abe..8876b9536ce92 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -22,7 +22,7 @@ import { TiledVectorLayer } from '../classes/layers/tiled_vector_layer/tiled_vec import { copyPersistentState, TRACKED_LAYER_DESCRIPTOR } from '../reducers/util'; import { InnerJoin } from '../classes/joins/inner_join'; import { getSourceByType } from '../classes/sources/source_registry'; -import { GeojsonFileSource } from '../classes/sources/geojson_file_source'; +import { GeoJsonFileSource } from '../classes/sources/geojson_file_source'; import { SOURCE_DATA_REQUEST_ID, STYLE_TYPE, @@ -241,10 +241,10 @@ export const getSpatialFiltersLayer = createSelector( type: 'FeatureCollection', features: extractFeaturesFromFilters(filters), }; - const geoJsonSourceDescriptor = GeojsonFileSource.createDescriptor( - featureCollection, - 'spatialFilters' - ); + const geoJsonSourceDescriptor = GeoJsonFileSource.createDescriptor({ + __featureCollection: featureCollection, + name: 'spatialFilters', + }); return new VectorLayer({ layerDescriptor: VectorLayer.createDescriptor({ @@ -272,7 +272,7 @@ export const getSpatialFiltersLayer = createSelector( }, }), }), - source: new GeojsonFileSource(geoJsonSourceDescriptor), + source: new GeoJsonFileSource(geoJsonSourceDescriptor), }); } ); From 3938fa67ad5b404da32b165f0cd8a40ec23765bf Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 26 Jan 2021 10:35:57 -0700 Subject: [PATCH 58/90] [Maps] allow saving maps to dashboards (#88759) * [Maps] allow saving maps to dashboards * update saveMap functional test method * update tags functional test * review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/kibana.json | 2 +- .../routes/map_page/saved_map/saved_map.ts | 15 ++- .../public/routes/map_page/top_nav_config.tsx | 108 ++++++++++-------- .../apps/maps/embeddable/save_and_return.js | 5 +- .../test/functional/page_objects/gis_page.ts | 18 ++- .../functional/tests/maps_integration.ts | 10 +- 6 files changed, 93 insertions(+), 65 deletions(-) diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index e47968b027cc3..42adf6f2a950b 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -23,5 +23,5 @@ "ui": true, "server": true, "extraPublicDirs": ["common/constants"], - "requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss"] + "requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss", "presentationUtil"] } diff --git a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts index 27fd78980710f..df424124be3f2 100644 --- a/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts +++ b/x-pack/plugins/maps/public/routes/map_page/saved_map/saved_map.ts @@ -281,10 +281,12 @@ export class SavedMap { returnToOrigin, newTags, saveByReference, + dashboardId, }: OnSaveProps & { - returnToOrigin: boolean; + returnToOrigin?: boolean; newTags?: string[]; saveByReference: boolean; + dashboardId?: string | null; }) { if (!this._attributes) { throw new Error('Invalid usage, must await whenReady before calling save'); @@ -337,7 +339,7 @@ export class SavedMap { }); return; } - this._getStateTransfer().navigateToWithEmbeddablePackage(this._originatingApp, { + await this._getStateTransfer().navigateToWithEmbeddablePackage(this._originatingApp, { state: { embeddableId: newCopyOnSave ? undefined : this._embeddableId, type: MAP_SAVED_OBJECT_TYPE, @@ -345,6 +347,15 @@ export class SavedMap { }, }); return; + } else if (dashboardId) { + await this._getStateTransfer().navigateToWithEmbeddablePackage('dashboards', { + state: { + type: MAP_SAVED_OBJECT_TYPE, + input: updatedMapEmbeddableInput, + }, + path: dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`, + }); + return; } this._mapEmbeddableInput = updatedMapEmbeddableInput; diff --git a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx index 43a74a9c73012..7010c281d24c6 100644 --- a/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routes/map_page/top_nav_config.tsx @@ -10,6 +10,7 @@ import { Adapters } from 'src/plugins/inspector/public'; import { getCoreChrome, getMapsCapabilities, + getIsAllowByValueEmbeddables, getInspector, getCoreI18n, getSavedObjectsClient, @@ -25,6 +26,7 @@ import { import { MAP_SAVED_OBJECT_TYPE } from '../../../common/constants'; import { SavedMap } from './saved_map'; import { getMapEmbeddableDisplayName } from '../../../common/i18n_getters'; +import { SavedObjectSaveModalDashboard } from '../../../../../../src/plugins/presentation_util/public'; export function getTopNavConfig({ savedMap, @@ -139,53 +141,67 @@ export function getTopNavConfig({ /> ) : undefined; - const saveModal = ( - { - try { - await checkForDuplicateTitle( - { - id: props.newCopyOnSave ? undefined : savedMap.getSavedObjectId(), - title: props.newTitle, - copyOnSave: props.newCopyOnSave, - lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '', - getEsType: () => MAP_SAVED_OBJECT_TYPE, - getDisplayName: getMapEmbeddableDisplayName, - }, - props.isTitleDuplicateConfirmed, - props.onTitleDuplicate, - { - savedObjectsClient: getSavedObjectsClient(), - overlays: getCoreOverlays(), - } - ); - } catch (e) { - // ignore duplicate title failure, user notified in save modal - return {}; - } + const saveModalProps = { + onSave: async ( + props: OnSaveProps & { returnToOrigin?: boolean; dashboardId?: string | null } + ) => { + try { + await checkForDuplicateTitle( + { + id: props.newCopyOnSave ? undefined : savedMap.getSavedObjectId(), + title: props.newTitle, + copyOnSave: props.newCopyOnSave, + lastSavedTitle: savedMap.getSavedObjectId() ? savedMap.getTitle() : '', + getEsType: () => MAP_SAVED_OBJECT_TYPE, + getDisplayName: getMapEmbeddableDisplayName, + }, + props.isTitleDuplicateConfirmed, + props.onTitleDuplicate, + { + savedObjectsClient: getSavedObjectsClient(), + overlays: getCoreOverlays(), + } + ); + } catch (e) { + // ignore duplicate title failure, user notified in save modal + return {}; + } + + await savedMap.save({ + ...props, + newTags: selectedTags, + saveByReference: !props.dashboardId, + }); + // showSaveModal wrapper requires onSave to return an object with an id to close the modal after successful save + return { id: 'id' }; + }, + onClose: () => {}, + documentInfo: { + description: mapDescription, + id: savedMap.getSavedObjectId(), + title: savedMap.getTitle(), + }, + objectType: i18n.translate('xpack.maps.topNav.saveModalType', { + defaultMessage: 'map', + }), + }; + + const saveModal = + savedMap.getOriginatingApp() || !getIsAllowByValueEmbeddables() ? ( + + ) : ( + + ); - await savedMap.save({ - ...props, - newTags: selectedTags, - saveByReference: true, - }); - // showSaveModal wrapper requires onSave to return an object with an id to close the modal after successful save - return { id: 'id' }; - }} - onClose={() => {}} - documentInfo={{ - description: mapDescription, - id: savedMap.getSavedObjectId(), - title: savedMap.getTitle(), - }} - objectType={i18n.translate('xpack.maps.topNav.saveModalType', { - defaultMessage: 'map', - })} - options={tagSelector} - /> - ); showSaveModal(saveModal, getCoreI18n().Context); }, }); diff --git a/x-pack/test/functional/apps/maps/embeddable/save_and_return.js b/x-pack/test/functional/apps/maps/embeddable/save_and_return.js index 40af8ddb9d44b..13aa08cbbeb38 100644 --- a/x-pack/test/functional/apps/maps/embeddable/save_and_return.js +++ b/x-pack/test/functional/apps/maps/embeddable/save_and_return.js @@ -31,6 +31,7 @@ export default function ({ getPageObjects, getService }) { after(async () => { await security.testUser.restoreDefaults(); }); + describe('new map', () => { beforeEach(async () => { await PageObjects.common.navigateToApp('dashboard'); @@ -55,7 +56,7 @@ export default function ({ getPageObjects, getService }) { it('should cut the originator and stay in maps application', async () => { await PageObjects.maps.saveMap( 'map created from dashboard save and return with originator app cut', - true + false ); await PageObjects.maps.waitForLayersToLoad(); await testSubjects.missingOrFail('mapSaveAndReturnButton'); @@ -94,7 +95,7 @@ export default function ({ getPageObjects, getService }) { describe('save as and uncheck return to origin switch', () => { it('should cut the originator and stay in maps application', async () => { - await PageObjects.maps.saveMap('Clone 2 of map embeddable example', true); + await PageObjects.maps.saveMap('Clone 2 of map embeddable example', false); await PageObjects.maps.waitForLayersToLoad(); await testSubjects.missingOrFail('mapSaveAndReturnButton'); await testSubjects.existOrFail('mapSaveButton'); diff --git a/x-pack/test/functional/page_objects/gis_page.ts b/x-pack/test/functional/page_objects/gis_page.ts index 7e22acf785d36..7767499cb6bd3 100644 --- a/x-pack/test/functional/page_objects/gis_page.ts +++ b/x-pack/test/functional/page_objects/gis_page.ts @@ -9,7 +9,7 @@ import { APP_ID } from '../../../plugins/maps/common/constants'; import { FtrProviderContext } from '../ftr_provider_context'; export function GisPageProvider({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['common', 'header', 'timePicker']); + const PageObjects = getPageObjects(['common', 'header', 'timePicker', 'visualize']); const log = getService('log'); const testSubjects = getService('testSubjects'); @@ -148,18 +148,14 @@ export function GisPageProvider({ getService, getPageObjects }: FtrProviderConte await renderable.waitForRender(); } - async saveMap(name: string, uncheckReturnToOriginModeSwitch = false, tags?: string[]) { + async saveMap(name: string, redirectToOrigin = true, tags?: string[]) { await testSubjects.click('mapSaveButton'); await testSubjects.setValue('savedObjectTitle', name); - if (uncheckReturnToOriginModeSwitch) { - const redirectToOriginCheckboxExists = await testSubjects.exists( - 'returnToOriginModeSwitch' - ); - if (!redirectToOriginCheckboxExists) { - throw new Error('Unable to uncheck "returnToOriginModeSwitch", it does not exist.'); - } - await testSubjects.setEuiSwitch('returnToOriginModeSwitch', 'uncheck'); - } + await PageObjects.visualize.setSaveModalValues(name, { + addToDashboard: false, + redirectToOrigin, + saveAsNew: true, + }); if (tags) { await testSubjects.click('savedObjectTagSelector'); for (const tagName of tags) { diff --git a/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts index 4e44659b4fc67..32b9cc378db45 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts @@ -13,7 +13,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const listingTable = getService('listingTable'); const testSubjects = getService('testSubjects'); const find = getService('find'); - const PageObjects = getPageObjects(['maps', 'tagManagement', 'common']); + const PageObjects = getPageObjects(['maps', 'tagManagement', 'common', 'visualize']); /** * Select tags in the searchbar's tag filter. @@ -78,7 +78,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('allows to select tags for a new map', async () => { - await PageObjects.maps.saveMap('my-new-map', false, ['tag-1', 'tag-3']); + await PageObjects.maps.saveMap('my-new-map', true, ['tag-1', 'tag-3']); await PageObjects.maps.gotoMapListingPage(); await selectFilterTags('tag-1'); @@ -91,6 +91,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.click('mapSaveButton'); await testSubjects.setValue('savedObjectTitle', 'map-with-new-tag'); + await PageObjects.visualize.setSaveModalValues('map-with-new-tag', { + addToDashboard: false, + saveAsNew: true, + }); await testSubjects.click('savedObjectTagSelector'); await testSubjects.click(`tagSelectorOption-action__create`); @@ -127,7 +131,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('allows to select tags for an existing map', async () => { await listingTable.clickItemLink('map', 'map 4 (tag-1)'); - await PageObjects.maps.saveMap('map 4 (tag-1)', false, ['tag-3']); + await PageObjects.maps.saveMap('map 4 (tag-1)', true, ['tag-3']); await PageObjects.maps.gotoMapListingPage(); await selectFilterTags('tag-3'); From eaf6831022991d17629c274aa2d11d8e0a1594db Mon Sep 17 00:00:00 2001 From: Michail Yasonik Date: Tue, 26 Jan 2021 12:48:22 -0500 Subject: [PATCH 59/90] Adding better aria-labels for global search and field search in Lens (#89215) --- .../public/components/__snapshots__/search_bar.test.tsx.snap | 2 +- .../global_search_bar/public/components/search_bar.tsx | 3 +++ .../lens/public/indexpattern_datasource/datapanel.tsx | 5 +++-- x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap b/x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap index f5e7a030d59e3..8433d98c232d6 100644 --- a/x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap +++ b/x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap @@ -33,7 +33,7 @@ exports[`SearchBar supports keyboard shortcuts 1`] = ` { setLocalState({ ...localState, nameFilter: e.target.value }); }} - aria-label={i18n.translate('xpack.lens.indexPatterns.filterByNameAriaLabel', { - defaultMessage: 'Search fields', + aria-label={i18n.translate('xpack.lens.indexPatterns.filterByNameLabel', { + defaultMessage: 'Search field names', + description: 'Search the list of fields in the index pattern for the provided text', })} aria-describedby={fieldSearchDescriptionId} /> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 93267c950c10e..e28b41e0ce5e3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11431,7 +11431,6 @@ "xpack.lens.indexPatterns.clearFiltersLabel": "名前とタイプフィルターを消去", "xpack.lens.indexPatterns.fieldFiltersLabel": "フィールドフィルター", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields}使用可能な{availableFields, plural, other {フィールド}}。{emptyFields}空の{emptyFields, plural, other {フィールド}}。 {metaFields}メタ{metaFields, plural, other {フィールド}}。", - "xpack.lens.indexPatterns.filterByNameAriaLabel": "検索フィールド", "xpack.lens.indexPatterns.filterByNameLabel": "検索フィールド名", "xpack.lens.indexPatterns.noAvailableDataLabel": "データを含むフィールドはありません。", "xpack.lens.indexPatterns.noDataLabel": "フィールドがありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 41bdf97333dd7..49fd9ce095f66 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11460,7 +11460,6 @@ "xpack.lens.indexPatterns.clearFiltersLabel": "清除名称和类型筛选", "xpack.lens.indexPatterns.fieldFiltersLabel": "字段筛选", "xpack.lens.indexPatterns.fieldSearchLiveRegion": "{availableFields} 个可用{availableFields, plural, other {字段}}。{emptyFields} 个空{emptyFields, plural, other {字段}}。{metaFields} 个元{metaFields, plural,other {字段}}。", - "xpack.lens.indexPatterns.filterByNameAriaLabel": "搜索字段", "xpack.lens.indexPatterns.filterByNameLabel": "搜索字段名称", "xpack.lens.indexPatterns.noAvailableDataLabel": "没有包含数据的可用字段。", "xpack.lens.indexPatterns.noDataLabel": "无字段。", From ecf512a048393a80a31f7085f0e7fc74a55b731f Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Tue, 26 Jan 2021 19:06:40 +0100 Subject: [PATCH 60/90] [Lens] Make Lens visualization load faster on Dashboard (#88953) * :rocket: Load indexPatternRefs only on edit mode * :white_mark_check: Fix test with new editor init flag * :bug: Avoid to save to localStorage undefined indexPattern * :white_mark_check: Adapted tests to new conditional ref loading Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../editor_frame/editor_frame.test.tsx | 8 ++++-- .../editor_frame/editor_frame.tsx | 3 +- .../editor_frame/state_helpers.ts | 15 ++++++++-- .../indexpattern_datasource/indexpattern.tsx | 5 +++- .../indexpattern_datasource/loader.test.ts | 28 +++++++++++++++++++ .../public/indexpattern_datasource/loader.ts | 17 +++++++---- x-pack/plugins/lens/public/types.ts | 7 ++++- 7 files changed, 70 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx index 76394c2901aaa..c0728bd030a0a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.test.tsx @@ -188,8 +188,12 @@ describe('editor_frame', () => { /> ); }); - expect(mockDatasource.initialize).toHaveBeenCalledWith(datasource1State, [], undefined); - expect(mockDatasource2.initialize).toHaveBeenCalledWith(datasource2State, [], undefined); + expect(mockDatasource.initialize).toHaveBeenCalledWith(datasource1State, [], undefined, { + isFullEditor: true, + }); + expect(mockDatasource2.initialize).toHaveBeenCalledWith(datasource2State, [], undefined, { + isFullEditor: true, + }); expect(mockDatasource3.initialize).not.toHaveBeenCalled(); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index f908d16afe470..b6df0caa07577 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -81,7 +81,8 @@ export function EditorFrame(props: EditorFrameProps) { props.datasourceMap, state.datasourceStates, props.doc?.references, - visualizeTriggerFieldContext + visualizeTriggerFieldContext, + { isFullEditor: true } ) .then((result) => { if (!isUnmounted) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 9c5eafc300abc..de747fde2e92c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -10,6 +10,7 @@ import { Datasource, DatasourcePublicAPI, FramePublicAPI, + InitializationOptions, Visualization, VisualizationDimensionGroupConfig, } from '../../types'; @@ -21,14 +22,20 @@ export async function initializeDatasources( datasourceMap: Record, datasourceStates: Record, references?: SavedObjectReference[], - initialContext?: VisualizeFieldContext + initialContext?: VisualizeFieldContext, + options?: InitializationOptions ) { const states: Record = {}; await Promise.all( Object.entries(datasourceMap).map(([datasourceId, datasource]) => { if (datasourceStates[datasourceId]) { return datasource - .initialize(datasourceStates[datasourceId].state || undefined, references, initialContext) + .initialize( + datasourceStates[datasourceId].state || undefined, + references, + initialContext, + options + ) .then((datasourceState) => { states[datasourceId] = { isLoading: false, state: datasourceState }; }); @@ -82,7 +89,9 @@ export async function persistedStateToExpression( { isLoading: false, state }, ]) ), - references + references, + undefined, + { isFullEditor: false } ); const datasourceLayers = createDatasourceLayers(datasources, datasourceStates); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 48592c44aa543..22c0054cb33e0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -18,6 +18,7 @@ import { Operation, DatasourceLayerPanelProps, PublicAPIProps, + InitializationOptions, } from '../types'; import { loadInitialState, @@ -104,7 +105,8 @@ export function getIndexPatternDatasource({ async initialize( persistedState?: IndexPatternPersistedState, references?: SavedObjectReference[], - initialContext?: VisualizeFieldContext + initialContext?: VisualizeFieldContext, + options?: InitializationOptions ) { return loadInitialState({ persistedState, @@ -114,6 +116,7 @@ export function getIndexPatternDatasource({ storage, indexPatternsService, initialContext, + options, }); }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 29786d9bc68f3..801496d1a5701 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -346,6 +346,7 @@ describe('loader', () => { savedObjectsClient: mockClient(), indexPatternsService: mockIndexPatternsService(), storage, + options: { isFullEditor: true }, }); expect(state).toMatchObject({ @@ -364,12 +365,35 @@ describe('loader', () => { }); }); + it('should load a default state without loading the indexPatterns when embedded', async () => { + const storage = createMockStorage(); + const savedObjectsClient = mockClient(); + const state = await loadInitialState({ + savedObjectsClient, + indexPatternsService: mockIndexPatternsService(), + storage, + options: { isFullEditor: false }, + }); + + expect(state).toMatchObject({ + currentIndexPatternId: undefined, + indexPatternRefs: [], + indexPatterns: {}, + layers: {}, + }); + + expect(storage.set).not.toHaveBeenCalled(); + + expect(savedObjectsClient.find).not.toHaveBeenCalled(); + }); + it('should load a default state when lastUsedIndexPatternId is not found in indexPatternRefs', async () => { const storage = createMockStorage({ indexPatternId: 'c' }); const state = await loadInitialState({ savedObjectsClient: mockClient(), indexPatternsService: mockIndexPatternsService(), storage, + options: { isFullEditor: true }, }); expect(state).toMatchObject({ @@ -393,6 +417,7 @@ describe('loader', () => { savedObjectsClient: mockClient(), indexPatternsService: mockIndexPatternsService(), storage: createMockStorage({ indexPatternId: '2' }), + options: { isFullEditor: true }, }); expect(state).toMatchObject({ @@ -415,6 +440,7 @@ describe('loader', () => { savedObjectsClient: mockClient(), indexPatternsService: mockIndexPatternsService(), storage, + options: { isFullEditor: true }, }); expect(state).toMatchObject({ @@ -443,6 +469,7 @@ describe('loader', () => { indexPatternId: '1', fieldName: '', }, + options: { isFullEditor: true }, }); expect(state).toMatchObject({ @@ -499,6 +526,7 @@ describe('loader', () => { savedObjectsClient: mockClient(), indexPatternsService: mockIndexPatternsService(), storage, + options: { isFullEditor: true }, }); expect(state).toMatchObject({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 64c4122245ce0..52505b12be4c3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { SavedObjectsClientContract, HttpSetup, SavedObjectReference } from 'kibana/public'; -import { StateSetter } from '../types'; +import { InitializationOptions, StateSetter } from '../types'; import { IndexPattern, IndexPatternRef, @@ -190,6 +190,7 @@ export async function loadInitialState({ storage, indexPatternsService, initialContext, + options, }: { persistedState?: IndexPatternPersistedState; references?: SavedObjectReference[]; @@ -198,8 +199,10 @@ export async function loadInitialState({ storage: IStorageWrapper; indexPatternsService: IndexPatternsService; initialContext?: VisualizeFieldContext; + options?: InitializationOptions; }): Promise { - const indexPatternRefs = await loadIndexPatternRefs(savedObjectsClient); + const { isFullEditor } = options ?? {}; + const indexPatternRefs = await (isFullEditor ? loadIndexPatternRefs(savedObjectsClient) : []); const lastUsedIndexPatternId = getLastUsedIndexPatternId(storage, indexPatternRefs); const state = @@ -210,11 +213,15 @@ export async function loadInitialState({ ? Object.values(state.layers) .map((l) => l.indexPatternId) .concat(state.currentIndexPatternId) - : [lastUsedIndexPatternId || defaultIndexPatternId || indexPatternRefs[0].id] - ); + : [lastUsedIndexPatternId || defaultIndexPatternId || indexPatternRefs[0]?.id] + ) + // take out the undefined from the list + .filter(Boolean); const currentIndexPatternId = initialContext?.indexPatternId ?? requiredPatterns[0]; - setLastUsedIndexPatternId(storage, currentIndexPatternId); + if (currentIndexPatternId) { + setLastUsedIndexPatternId(storage, currentIndexPatternId); + } const indexPatterns = await loadIndexPatterns({ indexPatternsService, diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index bba601f942380..9feed918635b3 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -139,6 +139,10 @@ export interface DatasourceSuggestion { export type StateSetter = (newState: T | ((prevState: T) => T)) => void; +export interface InitializationOptions { + isFullEditor?: boolean; +} + /** * Interface for the datasource registry */ @@ -151,7 +155,8 @@ export interface Datasource { initialize: ( state?: P, savedObjectReferences?: SavedObjectReference[], - initialContext?: VisualizeFieldContext + initialContext?: VisualizeFieldContext, + options?: InitializationOptions ) => Promise; // Given the current state, which parts should be saved? From 7bb8d3a7b244d3d3f8127a757f480ab6a9d7a312 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 26 Jan 2021 13:09:53 -0600 Subject: [PATCH 61/90] [Workplace Search] Fix Private Dashboard routes (#88985) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add index route for personal dashboard * Fix links to personal source flow In ent-search, the base route was /sources so the getSourcesPath helper was not needed. In Kibana, we use the ‘/p’ route to differentiate personal from org so the helper is needed and we pass false as the isOrganization flag * Remove legacy sidebar text When I first migrated this, I left the sidebar copy in so that it was not aboandoned before the design pass. After talking with John we decided to just use the copy to the right of the sidebar so this drops that legacy copy. * Remove constants * Remove legacy sidebar link * Revert "Remove legacy sidebar text" This reverts commit 8c8a3fb63c08154fbbdbe44533a8dd8ec6b265da. * Revert "Remove constants" This reverts commit a88723ec90d10e7b03214f176a2cab133e9d3ae5. * Revert "Remove legacy sidebar link" This reverts commit 5d08a12a7d53690805727f181967bd3d82b24235. * Update TODO Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/applications/workplace_search/index.tsx | 6 ++++++ .../views/content_sources/private_sources.tsx | 11 ++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index 65a2c7a4a44dd..d10de7a770171 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -78,6 +78,12 @@ export const WorkplaceSearchConfigured: React.FC = (props) => { {errorConnecting ? : } + + {/* TODO: replace Layout with PrivateSourcesLayout (needs to be created) */} + } restrictWidth readOnlyMode={readOnlyMode}> + + + } />} diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx index a1a76c678866c..c11cdaa5ec36f 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/content_sources/private_sources.tsx @@ -12,7 +12,7 @@ import { EuiCallOut, EuiEmptyPrompt, EuiSpacer, EuiPanel } from '@elastic/eui'; import { LicensingLogic } from '../../../../applications/shared/licensing'; -import { ADD_SOURCE_PATH } from '../../routes'; +import { ADD_SOURCE_PATH, getSourcesPath } from '../../routes'; import noSharedSourcesIcon from '../../assets/share_circle.svg'; @@ -74,12 +74,17 @@ export const PrivateSources: React.FC = () => { sidebarLinks.push({ title: PRIVATE_LINK_TITLE, iconType: 'plusInCircle', - path: ADD_SOURCE_PATH, + path: getSourcesPath(ADD_SOURCE_PATH, false), }); } const headerAction = ( - + {PRIVATE_LINK_TITLE} ); From 0c2c451830d7f8b8ecacd8b2500e44f1cf5d7741 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Tue, 26 Jan 2021 21:56:31 +0200 Subject: [PATCH 62/90] [Security Solution][Case] Add button to go to case view after adding an alert to a case (#89214) --- .../add_to_case_action.test.tsx | 64 ++++++++++++++++--- .../timeline_actions/add_to_case_action.tsx | 23 ++++++- .../{helpers.test.ts => helpers.test.tsx} | 22 ++----- .../{helpers.ts => helpers.tsx} | 22 ++++--- .../timeline_actions/toaster_content.test.tsx | 45 +++++++++++++ .../timeline_actions/toaster_content.tsx | 46 +++++++++++++ .../timeline_actions/translations.ts | 7 ++ 7 files changed, 195 insertions(+), 34 deletions(-) rename x-pack/plugins/security_solution/public/cases/components/timeline_actions/{helpers.test.ts => helpers.test.tsx} (55%) rename x-pack/plugins/security_solution/public/cases/components/timeline_actions/{helpers.ts => helpers.tsx} (59%) create mode 100644 x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.test.tsx create mode 100644 x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.tsx diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx index 0c156e247a5e0..71d7387d8d392 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.test.tsx @@ -6,18 +6,24 @@ /* eslint-disable react/display-name */ import React, { ReactNode } from 'react'; - import { mount } from 'enzyme'; +import { EuiGlobalToastList } from '@elastic/eui'; + +import { useKibana } from '../../../common/lib/kibana'; +import { useStateToaster } from '../../../common/components/toasters'; import { TestProviders } from '../../../common/mock'; import { usePostComment } from '../../containers/use_post_comment'; +import { Case } from '../../containers/types'; import { AddToCaseAction } from './add_to_case_action'; jest.mock('../../containers/use_post_comment'); -jest.mock('../../../common/lib/kibana', () => { - const originalModule = jest.requireActual('../../../common/lib/kibana'); +jest.mock('../../../common/lib/kibana'); + +jest.mock('../../../common/components/toasters', () => { + const actual = jest.requireActual('../../../common/components/toasters'); return { - ...originalModule, - useGetUserSavedObjectPermissions: jest.fn(), + ...actual, + useStateToaster: jest.fn(), }; }); @@ -44,14 +50,16 @@ jest.mock('../create/form_context', () => { onSuccess, }: { children: ReactNode; - onSuccess: ({ id }: { id: string }) => void; + onSuccess: (theCase: Partial) => void; }) => { return ( <> @@ -95,9 +103,16 @@ describe('AddToCaseAction', () => { disabled: false, }; + const mockDispatchToaster = jest.fn(); + const mockNavigateToApp = jest.fn(); + beforeEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); usePostCommentMock.mockImplementation(() => defaultPostComment); + (useStateToaster as jest.Mock).mockReturnValue([jest.fn(), mockDispatchToaster]); + (useKibana as jest.Mock).mockReturnValue({ + services: { application: { navigateToApp: mockNavigateToApp } }, + }); }); it('it renders', async () => { @@ -187,4 +202,37 @@ describe('AddToCaseAction', () => { type: 'alert', }); }); + + it('navigates to case view', async () => { + usePostCommentMock.mockImplementation(() => { + return { + ...defaultPostComment, + postComment: jest.fn().mockImplementation((caseId, data, updateCase) => updateCase()), + }; + }); + + const wrapper = mount( + + + + ); + + wrapper.find(`[data-test-subj="attach-alert-to-case-button"]`).first().simulate('click'); + wrapper.find(`[data-test-subj="add-new-case-item"]`).first().simulate('click'); + wrapper.find(`[data-test-subj="form-context-on-success"]`).first().simulate('click'); + + expect(mockDispatchToaster).toHaveBeenCalled(); + const toast = mockDispatchToaster.mock.calls[0][0].toast; + + const toastWrapper = mount( + {}} /> + ); + + toastWrapper + .find('[data-test-subj="toaster-content-case-view-link"]') + .first() + .simulate('click'); + + expect(mockNavigateToApp).toHaveBeenCalledWith('securitySolution:case', { path: '/new-case' }); + }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx index 3ebc0654fc019..eed4f2092fd58 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/add_to_case_action.tsx @@ -20,6 +20,10 @@ import { ActionIconItem } from '../../../timelines/components/timeline/body/acti import { usePostComment } from '../../containers/use_post_comment'; import { Case } from '../../containers/types'; import { useStateToaster } from '../../../common/components/toasters'; +import { APP_ID } from '../../../../common/constants'; +import { useKibana } from '../../../common/lib/kibana'; +import { getCaseDetailsUrl } from '../../../common/components/link_to'; +import { SecurityPageName } from '../../../app/types'; import { useCreateCaseModal } from '../use_create_case_modal'; import { useAllCasesModal } from '../use_all_cases_modal'; import { createUpdateSuccessToaster } from './helpers'; @@ -39,12 +43,23 @@ const AddToCaseActionComponent: React.FC = ({ const eventId = ecsRowData._id; const eventIndex = ecsRowData._index; + const { navigateToApp } = useKibana().services.application; const [, dispatchToaster] = useStateToaster(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const openPopover = useCallback(() => setIsPopoverOpen(true), []); const closePopover = useCallback(() => setIsPopoverOpen(false), []); const { postComment } = usePostComment(); + + const onViewCaseClick = useCallback( + (id) => { + navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCaseDetailsUrl({ id }), + }); + }, + [navigateToApp] + ); + const attachAlertToCase = useCallback( (theCase: Case) => { postComment( @@ -54,10 +69,14 @@ const AddToCaseActionComponent: React.FC = ({ alertId: eventId, index: eventIndex ?? '', }, - () => dispatchToaster({ type: 'addToaster', toast: createUpdateSuccessToaster(theCase) }) + () => + dispatchToaster({ + type: 'addToaster', + toast: createUpdateSuccessToaster(theCase, onViewCaseClick), + }) ); }, - [postComment, eventId, eventIndex, dispatchToaster] + [postComment, eventId, eventIndex, dispatchToaster, onViewCaseClick] ); const { modal: createCaseModal, openModal: openCreateCaseModal } = useCreateCaseModal({ diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.ts b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.tsx similarity index 55% rename from x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.ts rename to x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.tsx index 58c9c4baf82eb..b05dc4134cb10 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.test.tsx @@ -8,6 +8,7 @@ import { createUpdateSuccessToaster } from './helpers'; import { Case } from '../../containers/types'; const theCase = { + id: 'case-id', title: 'My case', settings: { syncAlerts: true, @@ -15,24 +16,13 @@ const theCase = { } as Case; describe('helpers', () => { + const onViewCaseClick = jest.fn(); + describe('createUpdateSuccessToaster', () => { it('creates the correct toast when the sync alerts is on', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster(theCase); - expect(toast).toEqual({ - color: 'success', - iconType: 'check', - text: 'Alerts in this case have their status synched with the case status', - title: 'An alert has been added to "My case"', - }); - }); - - it('creates the correct toast when the sync alerts is off', () => { - // We remove the id as is randomly generated - const { id, ...toast } = createUpdateSuccessToaster({ - ...theCase, - settings: { syncAlerts: false }, - }); + // We remove the id as is randomly generated and the text as it is a React component + // which is being test on toaster_content.test.tsx + const { id, text, ...toast } = createUpdateSuccessToaster(theCase, onViewCaseClick); expect(toast).toEqual({ color: 'success', iconType: 'check', diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.ts b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.tsx similarity index 59% rename from x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.ts rename to x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.tsx index abafa55c28903..b1bae8df0a0b1 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.ts +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/helpers.tsx @@ -4,22 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ +import React from 'react'; import uuid from 'uuid'; import { AppToast } from '../../../common/components/toasters'; import { Case } from '../../containers/types'; +import { ToasterContent } from './toaster_content'; import * as i18n from './translations'; -export const createUpdateSuccessToaster = (theCase: Case): AppToast => { - const toast: AppToast = { +export const createUpdateSuccessToaster = ( + theCase: Case, + onViewCaseClick: (id: string) => void +): AppToast => { + return { id: uuid.v4(), color: 'success', iconType: 'check', title: i18n.CASE_CREATED_SUCCESS_TOAST(theCase.title), + text: ( + + ), }; - - if (theCase.settings.syncAlerts) { - return { ...toast, text: i18n.CASE_CREATED_SUCCESS_TOAST_TEXT }; - } - - return toast; }; diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.test.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.test.tsx new file mode 100644 index 0000000000000..b04ebb8903aea --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.test.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mount } from 'enzyme'; + +import { ToasterContent } from './toaster_content'; + +describe('ToasterContent', () => { + const onViewCaseClick = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders with syncAlerts=true', () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="toaster-content-case-view-link"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="toaster-content-sync-text"]').exists()).toBeTruthy(); + }); + + it('renders with syncAlerts=false', () => { + const wrapper = mount( + + ); + + expect(wrapper.find('[data-test-subj="toaster-content-case-view-link"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="toaster-content-sync-text"]').exists()).toBeFalsy(); + }); + + it('calls onViewCaseClick', () => { + const wrapper = mount( + + ); + + wrapper.find('[data-test-subj="toaster-content-case-view-link"]').first().simulate('click'); + expect(onViewCaseClick).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.tsx b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.tsx new file mode 100644 index 0000000000000..871db464d8576 --- /dev/null +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/toaster_content.tsx @@ -0,0 +1,46 @@ +/* + * 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, { memo, useCallback } from 'react'; +import { EuiButtonEmpty, EuiText } from '@elastic/eui'; +import styled from 'styled-components'; + +import * as i18n from './translations'; + +const EuiTextStyled = styled(EuiText)` + ${({ theme }) => ` + margin-bottom: ${theme.eui?.paddingSizes?.s ?? 8}px; + `} +`; + +interface Props { + caseId: string; + syncAlerts: boolean; + onViewCaseClick: (id: string) => void; +} + +const ToasterContentComponent: React.FC = ({ caseId, syncAlerts, onViewCaseClick }) => { + const onClick = useCallback(() => onViewCaseClick(caseId), [caseId, onViewCaseClick]); + return ( + <> + {syncAlerts && ( + + {i18n.CASE_CREATED_SUCCESS_TOAST_TEXT} + + )} + + {i18n.VIEW_CASE} + + + ); +}; + +export const ToasterContent = memo(ToasterContentComponent); diff --git a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/translations.ts b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/translations.ts index 479323ed1301c..dd3d6f0b50f19 100644 --- a/x-pack/plugins/security_solution/public/cases/components/timeline_actions/translations.ts +++ b/x-pack/plugins/security_solution/public/cases/components/timeline_actions/translations.ts @@ -53,3 +53,10 @@ export const CASE_CREATED_SUCCESS_TOAST_TEXT = i18n.translate( defaultMessage: 'Alerts in this case have their status synched with the case status', } ); + +export const VIEW_CASE = i18n.translate( + 'xpack.securitySolution.case.timeline.actions.caseCreatedSuccessToastViewCaseLink', + { + defaultMessage: 'View Case', + } +); From 87992d01da5787c0dd21f1186e8e8926cac97f07 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 26 Jan 2021 12:49:44 -0800 Subject: [PATCH 63/90] Watcher -functional xpack test using test_user with specific permissions. (#89068) * fixes https://github.com/elastic/kibana/issues/74449 * watcher test with specific permissions * adding the false parameter Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/functional/apps/watcher/watcher_test.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/x-pack/test/functional/apps/watcher/watcher_test.js b/x-pack/test/functional/apps/watcher/watcher_test.js index 1dd3fb6bbcc3d..2525f4d8f9621 100644 --- a/x-pack/test/functional/apps/watcher/watcher_test.js +++ b/x-pack/test/functional/apps/watcher/watcher_test.js @@ -15,16 +15,15 @@ export default function ({ getService, getPageObjects }) { const retry = getService('retry'); const testSubjects = getService('testSubjects'); const log = getService('log'); + const security = getService('security'); const esSupertest = getService('esSupertest'); const PageObjects = getPageObjects(['security', 'common', 'header', 'settings', 'watcher']); - // Still flaky test :c - // https://github.com/elastic/kibana/pull/56361 - // https://github.com/elastic/kibana/pull/56304 - describe.skip('watcher_test', function () { + describe('watcher_test', function () { before('initialize tests', async () => { // There may be system watches if monitoring was previously enabled // These cannot be deleted via the UI, so we need to delete via the API + await security.testUser.setRoles(['kibana_admin', 'watcher_admin'], false); const watches = await esSupertest.get('/.watches/_search'); if (watches.status === 200) { @@ -56,6 +55,10 @@ export default function ({ getService, getPageObjects }) { }); }); + after(async () => { + await security.testUser.restoreDefaults(); + }); + it('create and save a new watch', async () => { await PageObjects.watcher.createWatch(watchID, watchName); const watch = await PageObjects.watcher.getWatch(watchID); From 0b6184769622e1e13c4890c3f7a9711b196e403c Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 26 Jan 2021 14:32:09 -0700 Subject: [PATCH 64/90] [Maps] rename file_upload to maps_file_upload (#89225) * migrate file_upload plugin to maps_file_upload * update plugins list * results of running node scripts/build_plugin_list_docs * fix build problems * remove fileUpload from limits.yml Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 8 ++++---- packages/kbn-optimizer/limits.yml | 2 +- x-pack/.i18nrc.json | 2 +- x-pack/plugins/file_upload/README.md | 3 --- x-pack/plugins/maps/kibana.json | 2 +- .../public/classes/layers/file_upload_wizard/wizard.tsx | 2 +- x-pack/plugins/maps/public/kibana_services.ts | 2 +- x-pack/plugins/maps/public/plugin.ts | 4 ++-- x-pack/plugins/maps_file_upload/README.md | 3 +++ .../common/constants/file_import.ts | 0 .../{file_upload => maps_file_upload}/jest.config.js | 2 +- .../plugins/{file_upload => maps_file_upload}/kibana.json | 2 +- .../plugins/{file_upload => maps_file_upload}/mappings.ts | 0 .../public/components/index_settings.js | 0 .../public/components/json_import_progress.js | 0 .../public/components/json_index_file_picker.js | 0 .../public/components/json_upload_and_parse.js | 0 .../public/get_file_upload_component.ts | 0 .../{file_upload => maps_file_upload}/public/index.ts | 0 .../public/kibana_services.js | 0 .../{file_upload => maps_file_upload}/public/plugin.ts | 0 .../public/util/file_parser.js | 0 .../public/util/file_parser.test.js | 0 .../public/util/geo_json_clean_and_validate.js | 0 .../public/util/geo_json_clean_and_validate.test.js | 0 .../public/util/geo_processing.js | 0 .../public/util/geo_processing.test.js | 0 .../public/util/http_service.js | 0 .../public/util/indexing_service.js | 0 .../public/util/indexing_service.test.js | 0 .../public/util/size_limited_chunking.js | 0 .../public/util/size_limited_chunking.test.js | 0 .../server/client/errors.js | 0 .../{file_upload => maps_file_upload}/server/index.js | 0 .../server/kibana_server_services.js | 0 .../server/models/import_data/import_data.js | 0 .../server/models/import_data/index.js | 0 .../{file_upload => maps_file_upload}/server/plugin.js | 0 .../server/routes/file_upload.js | 0 .../server/routes/file_upload.test.js | 0 .../server/telemetry/file_upload_usage_collector.ts | 0 .../server/telemetry/index.ts | 0 .../server/telemetry/mappings.ts | 0 .../server/telemetry/telemetry.test.ts | 0 .../server/telemetry/telemetry.ts | 0 45 files changed, 16 insertions(+), 16 deletions(-) delete mode 100644 x-pack/plugins/file_upload/README.md create mode 100644 x-pack/plugins/maps_file_upload/README.md rename x-pack/plugins/{file_upload => maps_file_upload}/common/constants/file_import.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/jest.config.js (84%) rename x-pack/plugins/{file_upload => maps_file_upload}/kibana.json (83%) rename x-pack/plugins/{file_upload => maps_file_upload}/mappings.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/components/index_settings.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/components/json_import_progress.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/components/json_index_file_picker.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/components/json_upload_and_parse.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/get_file_upload_component.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/index.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/kibana_services.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/plugin.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/file_parser.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/file_parser.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/geo_json_clean_and_validate.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/geo_json_clean_and_validate.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/geo_processing.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/geo_processing.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/http_service.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/indexing_service.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/indexing_service.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/size_limited_chunking.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/public/util/size_limited_chunking.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/client/errors.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/index.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/kibana_server_services.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/models/import_data/import_data.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/models/import_data/index.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/plugin.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/routes/file_upload.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/routes/file_upload.test.js (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/telemetry/file_upload_usage_collector.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/telemetry/index.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/telemetry/mappings.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/telemetry/telemetry.test.ts (100%) rename x-pack/plugins/{file_upload => maps_file_upload}/server/telemetry/telemetry.ts (100%) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index ef3492a545b6a..7ce4896a8bce4 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -380,10 +380,6 @@ and actions. |The features plugin enhance Kibana with a per-feature privilege system. -|{kib-repo}blob/{branch}/x-pack/plugins/file_upload/README.md[fileUpload] -|Backend and core front-end react-components for GeoJson file upload. Only supports the Maps plugin. - - |{kib-repo}blob/{branch}/x-pack/plugins/fleet/README.md[fleet] |Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.fleet.agents.tlsCheckDisabled=false) @@ -453,6 +449,10 @@ using the CURL scripts in the scripts folder. |Visualize geo data from Elasticsearch or 3rd party geo-services. +|{kib-repo}blob/{branch}/x-pack/plugins/maps_file_upload/README.md[mapsFileUpload] +|Deprecated - plugin targeted for removal and will get merged into file_upload plugin + + |{kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing] |This plugin provides access to the detailed tile map services from Elastic. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 44cc4fdabb25e..ef672d6cbeb2e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -25,7 +25,6 @@ pageLoadAssetSize: esUiShared: 326654 expressions: 224136 features: 31211 - fileUpload: 24717 globalSearch: 43548 globalSearchBar: 62888 globalSearchProviders: 25554 @@ -106,3 +105,4 @@ pageLoadAssetSize: stackAlerts: 29684 presentationUtil: 28545 spacesOss: 18817 + mapsFileUpload: 23775 diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 6937862d20536..bfac437f3500a 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -20,7 +20,7 @@ "xpack.endpoint": "plugins/endpoint", "xpack.enterpriseSearch": "plugins/enterprise_search", "xpack.features": "plugins/features", - "xpack.fileUpload": "plugins/file_upload", + "xpack.fileUpload": "plugins/maps_file_upload", "xpack.globalSearch": ["plugins/global_search"], "xpack.globalSearchBar": ["plugins/global_search_bar"], "xpack.graph": ["plugins/graph"], diff --git a/x-pack/plugins/file_upload/README.md b/x-pack/plugins/file_upload/README.md deleted file mode 100644 index 0d4b4da61ccf6..0000000000000 --- a/x-pack/plugins/file_upload/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# File upload - -Backend and core front-end react-components for GeoJson file upload. Only supports the Maps plugin. \ No newline at end of file diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index 42adf6f2a950b..5f6f5224e32a3 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -8,7 +8,7 @@ "features", "inspector", "data", - "fileUpload", + "mapsFileUpload", "uiActions", "navigation", "visualizations", diff --git a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx index 68fd25ce9e7ae..54e58c876a839 100644 --- a/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx +++ b/x-pack/plugins/maps/public/classes/layers/file_upload_wizard/wizard.tsx @@ -18,7 +18,7 @@ import { GeoJsonFileSource } from '../../sources/geojson_file_source'; import { VectorLayer } from '../../layers/vector_layer/vector_layer'; import { createDefaultLayerDescriptor } from '../../sources/es_search_source'; import { RenderWizardArguments } from '../../layers/layer_wizard_registry'; -import { FileUploadComponentProps } from '../../../../../file_upload/public'; +import { FileUploadComponentProps } from '../../../../../maps_file_upload/public'; export const INDEX_SETUP_STEP_ID = 'INDEX_SETUP_STEP_ID'; export const INDEXING_STEP_ID = 'INDEXING_STEP_ID'; diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index 02b875257a5ac..99c9311a2a454 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -25,7 +25,7 @@ export const getIndexPatternService = () => pluginsStart.data.indexPatterns; export const getAutocompleteService = () => pluginsStart.data.autocomplete; export const getInspector = () => pluginsStart.inspector; export const getFileUploadComponent = async () => { - return await pluginsStart.fileUpload.getFileUploadComponent(); + return await pluginsStart.mapsFileUpload.getFileUploadComponent(); }; export const getUiSettings = () => coreStart.uiSettings; export const getIsDarkMode = () => getUiSettings().get('theme:darkMode', false); diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 690002a771601..dd256126fae62 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -54,7 +54,7 @@ import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { MapsLegacyConfig } from '../../../../src/plugins/maps_legacy/config'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; -import { StartContract as FileUploadStartContract } from '../../file_upload/public'; +import { StartContract as FileUploadStartContract } from '../../maps_file_upload/public'; import { SavedObjectsStart } from '../../../../src/plugins/saved_objects/public'; import { getIsEnterprisePlus, @@ -77,7 +77,7 @@ export interface MapsPluginSetupDependencies { export interface MapsPluginStartDependencies { data: DataPublicPluginStart; embeddable: EmbeddableStart; - fileUpload: FileUploadStartContract; + mapsFileUpload: FileUploadStartContract; inspector: InspectorStartContract; licensing: LicensingPluginStart; navigation: NavigationPublicPluginStart; diff --git a/x-pack/plugins/maps_file_upload/README.md b/x-pack/plugins/maps_file_upload/README.md new file mode 100644 index 0000000000000..1e3343664afb8 --- /dev/null +++ b/x-pack/plugins/maps_file_upload/README.md @@ -0,0 +1,3 @@ +# Maps File upload + +Deprecated - plugin targeted for removal and will get merged into file_upload plugin diff --git a/x-pack/plugins/file_upload/common/constants/file_import.ts b/x-pack/plugins/maps_file_upload/common/constants/file_import.ts similarity index 100% rename from x-pack/plugins/file_upload/common/constants/file_import.ts rename to x-pack/plugins/maps_file_upload/common/constants/file_import.ts diff --git a/x-pack/plugins/file_upload/jest.config.js b/x-pack/plugins/maps_file_upload/jest.config.js similarity index 84% rename from x-pack/plugins/file_upload/jest.config.js rename to x-pack/plugins/maps_file_upload/jest.config.js index 6a042a4cc5c1e..2893da079141c 100644 --- a/x-pack/plugins/file_upload/jest.config.js +++ b/x-pack/plugins/maps_file_upload/jest.config.js @@ -7,5 +7,5 @@ module.exports = { preset: '@kbn/test', rootDir: '../../..', - roots: ['/x-pack/plugins/file_upload'], + roots: ['/x-pack/plugins/maps_file_upload'], }; diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/maps_file_upload/kibana.json similarity index 83% rename from x-pack/plugins/file_upload/kibana.json rename to x-pack/plugins/maps_file_upload/kibana.json index 7676a01d0b0f9..f544c56cba517 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/maps_file_upload/kibana.json @@ -1,5 +1,5 @@ { - "id": "fileUpload", + "id": "mapsFileUpload", "version": "8.0.0", "kibanaVersion": "kibana", "server": true, diff --git a/x-pack/plugins/file_upload/mappings.ts b/x-pack/plugins/maps_file_upload/mappings.ts similarity index 100% rename from x-pack/plugins/file_upload/mappings.ts rename to x-pack/plugins/maps_file_upload/mappings.ts diff --git a/x-pack/plugins/file_upload/public/components/index_settings.js b/x-pack/plugins/maps_file_upload/public/components/index_settings.js similarity index 100% rename from x-pack/plugins/file_upload/public/components/index_settings.js rename to x-pack/plugins/maps_file_upload/public/components/index_settings.js diff --git a/x-pack/plugins/file_upload/public/components/json_import_progress.js b/x-pack/plugins/maps_file_upload/public/components/json_import_progress.js similarity index 100% rename from x-pack/plugins/file_upload/public/components/json_import_progress.js rename to x-pack/plugins/maps_file_upload/public/components/json_import_progress.js diff --git a/x-pack/plugins/file_upload/public/components/json_index_file_picker.js b/x-pack/plugins/maps_file_upload/public/components/json_index_file_picker.js similarity index 100% rename from x-pack/plugins/file_upload/public/components/json_index_file_picker.js rename to x-pack/plugins/maps_file_upload/public/components/json_index_file_picker.js diff --git a/x-pack/plugins/file_upload/public/components/json_upload_and_parse.js b/x-pack/plugins/maps_file_upload/public/components/json_upload_and_parse.js similarity index 100% rename from x-pack/plugins/file_upload/public/components/json_upload_and_parse.js rename to x-pack/plugins/maps_file_upload/public/components/json_upload_and_parse.js diff --git a/x-pack/plugins/file_upload/public/get_file_upload_component.ts b/x-pack/plugins/maps_file_upload/public/get_file_upload_component.ts similarity index 100% rename from x-pack/plugins/file_upload/public/get_file_upload_component.ts rename to x-pack/plugins/maps_file_upload/public/get_file_upload_component.ts diff --git a/x-pack/plugins/file_upload/public/index.ts b/x-pack/plugins/maps_file_upload/public/index.ts similarity index 100% rename from x-pack/plugins/file_upload/public/index.ts rename to x-pack/plugins/maps_file_upload/public/index.ts diff --git a/x-pack/plugins/file_upload/public/kibana_services.js b/x-pack/plugins/maps_file_upload/public/kibana_services.js similarity index 100% rename from x-pack/plugins/file_upload/public/kibana_services.js rename to x-pack/plugins/maps_file_upload/public/kibana_services.js diff --git a/x-pack/plugins/file_upload/public/plugin.ts b/x-pack/plugins/maps_file_upload/public/plugin.ts similarity index 100% rename from x-pack/plugins/file_upload/public/plugin.ts rename to x-pack/plugins/maps_file_upload/public/plugin.ts diff --git a/x-pack/plugins/file_upload/public/util/file_parser.js b/x-pack/plugins/maps_file_upload/public/util/file_parser.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/file_parser.js rename to x-pack/plugins/maps_file_upload/public/util/file_parser.js diff --git a/x-pack/plugins/file_upload/public/util/file_parser.test.js b/x-pack/plugins/maps_file_upload/public/util/file_parser.test.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/file_parser.test.js rename to x-pack/plugins/maps_file_upload/public/util/file_parser.test.js diff --git a/x-pack/plugins/file_upload/public/util/geo_json_clean_and_validate.js b/x-pack/plugins/maps_file_upload/public/util/geo_json_clean_and_validate.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/geo_json_clean_and_validate.js rename to x-pack/plugins/maps_file_upload/public/util/geo_json_clean_and_validate.js diff --git a/x-pack/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js b/x-pack/plugins/maps_file_upload/public/util/geo_json_clean_and_validate.test.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/geo_json_clean_and_validate.test.js rename to x-pack/plugins/maps_file_upload/public/util/geo_json_clean_and_validate.test.js diff --git a/x-pack/plugins/file_upload/public/util/geo_processing.js b/x-pack/plugins/maps_file_upload/public/util/geo_processing.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/geo_processing.js rename to x-pack/plugins/maps_file_upload/public/util/geo_processing.js diff --git a/x-pack/plugins/file_upload/public/util/geo_processing.test.js b/x-pack/plugins/maps_file_upload/public/util/geo_processing.test.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/geo_processing.test.js rename to x-pack/plugins/maps_file_upload/public/util/geo_processing.test.js diff --git a/x-pack/plugins/file_upload/public/util/http_service.js b/x-pack/plugins/maps_file_upload/public/util/http_service.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/http_service.js rename to x-pack/plugins/maps_file_upload/public/util/http_service.js diff --git a/x-pack/plugins/file_upload/public/util/indexing_service.js b/x-pack/plugins/maps_file_upload/public/util/indexing_service.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/indexing_service.js rename to x-pack/plugins/maps_file_upload/public/util/indexing_service.js diff --git a/x-pack/plugins/file_upload/public/util/indexing_service.test.js b/x-pack/plugins/maps_file_upload/public/util/indexing_service.test.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/indexing_service.test.js rename to x-pack/plugins/maps_file_upload/public/util/indexing_service.test.js diff --git a/x-pack/plugins/file_upload/public/util/size_limited_chunking.js b/x-pack/plugins/maps_file_upload/public/util/size_limited_chunking.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/size_limited_chunking.js rename to x-pack/plugins/maps_file_upload/public/util/size_limited_chunking.js diff --git a/x-pack/plugins/file_upload/public/util/size_limited_chunking.test.js b/x-pack/plugins/maps_file_upload/public/util/size_limited_chunking.test.js similarity index 100% rename from x-pack/plugins/file_upload/public/util/size_limited_chunking.test.js rename to x-pack/plugins/maps_file_upload/public/util/size_limited_chunking.test.js diff --git a/x-pack/plugins/file_upload/server/client/errors.js b/x-pack/plugins/maps_file_upload/server/client/errors.js similarity index 100% rename from x-pack/plugins/file_upload/server/client/errors.js rename to x-pack/plugins/maps_file_upload/server/client/errors.js diff --git a/x-pack/plugins/file_upload/server/index.js b/x-pack/plugins/maps_file_upload/server/index.js similarity index 100% rename from x-pack/plugins/file_upload/server/index.js rename to x-pack/plugins/maps_file_upload/server/index.js diff --git a/x-pack/plugins/file_upload/server/kibana_server_services.js b/x-pack/plugins/maps_file_upload/server/kibana_server_services.js similarity index 100% rename from x-pack/plugins/file_upload/server/kibana_server_services.js rename to x-pack/plugins/maps_file_upload/server/kibana_server_services.js diff --git a/x-pack/plugins/file_upload/server/models/import_data/import_data.js b/x-pack/plugins/maps_file_upload/server/models/import_data/import_data.js similarity index 100% rename from x-pack/plugins/file_upload/server/models/import_data/import_data.js rename to x-pack/plugins/maps_file_upload/server/models/import_data/import_data.js diff --git a/x-pack/plugins/file_upload/server/models/import_data/index.js b/x-pack/plugins/maps_file_upload/server/models/import_data/index.js similarity index 100% rename from x-pack/plugins/file_upload/server/models/import_data/index.js rename to x-pack/plugins/maps_file_upload/server/models/import_data/index.js diff --git a/x-pack/plugins/file_upload/server/plugin.js b/x-pack/plugins/maps_file_upload/server/plugin.js similarity index 100% rename from x-pack/plugins/file_upload/server/plugin.js rename to x-pack/plugins/maps_file_upload/server/plugin.js diff --git a/x-pack/plugins/file_upload/server/routes/file_upload.js b/x-pack/plugins/maps_file_upload/server/routes/file_upload.js similarity index 100% rename from x-pack/plugins/file_upload/server/routes/file_upload.js rename to x-pack/plugins/maps_file_upload/server/routes/file_upload.js diff --git a/x-pack/plugins/file_upload/server/routes/file_upload.test.js b/x-pack/plugins/maps_file_upload/server/routes/file_upload.test.js similarity index 100% rename from x-pack/plugins/file_upload/server/routes/file_upload.test.js rename to x-pack/plugins/maps_file_upload/server/routes/file_upload.test.js diff --git a/x-pack/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts b/x-pack/plugins/maps_file_upload/server/telemetry/file_upload_usage_collector.ts similarity index 100% rename from x-pack/plugins/file_upload/server/telemetry/file_upload_usage_collector.ts rename to x-pack/plugins/maps_file_upload/server/telemetry/file_upload_usage_collector.ts diff --git a/x-pack/plugins/file_upload/server/telemetry/index.ts b/x-pack/plugins/maps_file_upload/server/telemetry/index.ts similarity index 100% rename from x-pack/plugins/file_upload/server/telemetry/index.ts rename to x-pack/plugins/maps_file_upload/server/telemetry/index.ts diff --git a/x-pack/plugins/file_upload/server/telemetry/mappings.ts b/x-pack/plugins/maps_file_upload/server/telemetry/mappings.ts similarity index 100% rename from x-pack/plugins/file_upload/server/telemetry/mappings.ts rename to x-pack/plugins/maps_file_upload/server/telemetry/mappings.ts diff --git a/x-pack/plugins/file_upload/server/telemetry/telemetry.test.ts b/x-pack/plugins/maps_file_upload/server/telemetry/telemetry.test.ts similarity index 100% rename from x-pack/plugins/file_upload/server/telemetry/telemetry.test.ts rename to x-pack/plugins/maps_file_upload/server/telemetry/telemetry.test.ts diff --git a/x-pack/plugins/file_upload/server/telemetry/telemetry.ts b/x-pack/plugins/maps_file_upload/server/telemetry/telemetry.ts similarity index 100% rename from x-pack/plugins/file_upload/server/telemetry/telemetry.ts rename to x-pack/plugins/maps_file_upload/server/telemetry/telemetry.ts From 46285b2257c5ad11add7412e4145d842e3f91b83 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Tue, 26 Jan 2021 22:34:43 +0100 Subject: [PATCH 65/90] [ILM] Timeline component (#88024) * cleaning up unused types and legacy logic * added new relative age logic with unit tests * initial implementation of timeline * added custom infinity icon to timeline component * added comment * move timeline color bar comment * fix nanoseconds and microsecnds bug * added policy timeline heading, removed "at least" copy for now * a few minor changes - fix up copy - fix up responsive/mobile first view of timeline - adjust minimum size of a color bar * minor refactor to css classnames and make trash can for delete more prominent * added delete icon tooltip with rough first copy * added smoke test for timeline and how it interacts with different policy states * update test and copy * convert string svg to react svg component and use euiIcon class and refactor scss * update delete icon tooltip copy and add aria-label to svg * mock EuiIcon component in jest tests because it causes issues with custom react-svg component * added comment to mock * remove setting of classname * fix typo and update delete icon tooltip content * refinements to the delete icon at the end of the timline and support dark mode Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../client_integration/app/app.test.ts | 9 + .../edit_policy/edit_policy.helpers.tsx | 7 + .../edit_policy/edit_policy.test.ts | 46 ++++ .../__jest__/components/edit_policy.test.tsx | 9 + .../common/types/policies.ts | 2 + .../sections/edit_policy/components/index.ts | 1 + .../edit_policy/components/timeline/index.ts | 6 + .../components/timeline/infinity_icon.svg.tsx | 26 +++ .../components/timeline/timeline.scss | 87 ++++++++ .../components/timeline/timeline.tsx | 208 ++++++++++++++++++ .../sections/edit_policy/edit_policy.tsx | 7 +- 11 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/index.ts create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/infinity_icon.svg.tsx create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss create mode 100644 x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts index 9052cf2847baa..1ffbae39d3705 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/app/app.test.ts @@ -23,6 +23,15 @@ const PERCENT_SIGN_25_SEQUENCE = 'test%25'; window.scrollTo = jest.fn(); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + + return { + ...original, + EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. + }; +}); + describe('', () => { let testBed: AppTestBed; const { server, httpRequestsMockHelpers } = setupEnvironment(); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx index 7206fbfd547d4..72a0372628a22 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.helpers.tsx @@ -41,6 +41,7 @@ jest.mock('@elastic/eui', () => { }} /> ), + EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. }; }); @@ -236,6 +237,12 @@ export const setup = async (arg?: { appServicesContext: Partial exists('ilmTimelineHotPhase'), + hasWarmPhase: () => exists('ilmTimelineWarmPhase'), + hasColdPhase: () => exists('ilmTimelineColdPhase'), + hasDeletePhase: () => exists('ilmTimelineDeletePhase'), + }, hot: { setMaxSize, setMaxDocs, diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts index ff070a7f08bb1..e42e503fb853a 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts +++ b/x-pack/plugins/index_lifecycle_management/__jest__/client_integration/edit_policy/edit_policy.test.ts @@ -824,4 +824,50 @@ describe('', () => { expect(actions.cold.searchableSnapshotDisabledDueToRollover()).toBeTruthy(); }); }); + + describe('policy timeline', () => { + beforeEach(async () => { + httpRequestsMockHelpers.setLoadPolicies([getDefaultHotPhasePolicy('my_policy')]); + httpRequestsMockHelpers.setListNodes({ + nodesByRoles: {}, + nodesByAttributes: { test: ['123'] }, + isUsingDeprecatedDataRoleConfig: false, + }); + httpRequestsMockHelpers.setLoadSnapshotPolicies([]); + + await act(async () => { + testBed = await setup(); + }); + + const { component } = testBed; + component.update(); + }); + + test('showing all phases on the timeline', async () => { + const { actions } = testBed; + // This is how the default policy should look + expect(actions.timeline.hasHotPhase()).toBe(true); + expect(actions.timeline.hasWarmPhase()).toBe(false); + expect(actions.timeline.hasColdPhase()).toBe(false); + expect(actions.timeline.hasDeletePhase()).toBe(false); + + await actions.warm.enable(true); + expect(actions.timeline.hasHotPhase()).toBe(true); + expect(actions.timeline.hasWarmPhase()).toBe(true); + expect(actions.timeline.hasColdPhase()).toBe(false); + expect(actions.timeline.hasDeletePhase()).toBe(false); + + await actions.cold.enable(true); + expect(actions.timeline.hasHotPhase()).toBe(true); + expect(actions.timeline.hasWarmPhase()).toBe(true); + expect(actions.timeline.hasColdPhase()).toBe(true); + expect(actions.timeline.hasDeletePhase()).toBe(false); + + await actions.delete.enable(true); + expect(actions.timeline.hasHotPhase()).toBe(true); + expect(actions.timeline.hasWarmPhase()).toBe(true); + expect(actions.timeline.hasColdPhase()).toBe(true); + expect(actions.timeline.hasDeletePhase()).toBe(true); + }); + }); }); diff --git a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx index c54ccb9f85edf..6aa6c3177ca5d 100644 --- a/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx +++ b/x-pack/plugins/index_lifecycle_management/__jest__/components/edit_policy.test.tsx @@ -81,6 +81,15 @@ for (let i = 0; i < 105; i++) { } window.scrollTo = jest.fn(); +jest.mock('@elastic/eui', () => { + const original = jest.requireActual('@elastic/eui'); + + return { + ...original, + EuiIcon: 'eui-icon', // using custom react-svg icon causes issues, mocking for now. + }; +}); + let component: ReactElement; const activatePhase = async (rendered: ReactWrapper, phase: string) => { const testSubject = `enablePhaseSwitch-${phase}`; diff --git a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts index 1f4b06e80c49f..a084f16226fda 100644 --- a/x-pack/plugins/index_lifecycle_management/common/types/policies.ts +++ b/x-pack/plugins/index_lifecycle_management/common/types/policies.ts @@ -20,6 +20,8 @@ export interface Phases { delete?: SerializedDeletePhase; } +export type PhasesExceptDelete = keyof Omit; + export interface PolicyFromES { modified_date: string; name: string; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts index fa550214db477..d22206d7ae4de 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/index.ts @@ -11,5 +11,6 @@ export { OptionalLabel } from './optional_label'; export { PolicyJsonFlyout } from './policy_json_flyout'; export { DescribedFormRow, ToggleFieldWithDescribedFormRow } from './described_form_row'; export { FieldLoadingError } from './field_loading_error'; +export { Timeline } from './timeline'; export * from './phases'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/index.ts b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/index.ts new file mode 100644 index 0000000000000..4664429db37d7 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +export { Timeline } from './timeline'; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/infinity_icon.svg.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/infinity_icon.svg.tsx new file mode 100644 index 0000000000000..b3b1b3cc56b3d --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/infinity_icon.svg.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; + +export const InfinityIconSvg: FunctionComponent = (props) => { + return ( + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss new file mode 100644 index 0000000000000..452221a29a991 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.scss @@ -0,0 +1,87 @@ +$ilmTimelineBarHeight: $euiSizeS; + +/* +* For theming we need to shade or tint to get the right color from the base EUI color +*/ +$ilmDeletePhaseBackgroundColor: tintOrShade($euiColorVis5_behindText, 80%,80%); +$ilmDeletePhaseColor: shadeOrTint($euiColorVis5, 40%, 40%); + +.ilmTimeline { + overflow: hidden; + width: 100%; + + &__phasesContainer { + /* + * Let the delete icon sit on the same line as the phase color bars + */ + display: inline-block; + width: 100%; + + &__phase:first-child { + padding-left: 0; + padding-right: $euiSizeS; + } + + &__phase:last-child { + padding-left: $euiSizeS; + padding-right: 0; + } + + &__phase:only-child { + padding-left: 0; + padding-right: 0; + } + + &__phase { + /* + * Let the phase color bars sit horizontally + */ + display: inline-block; + + padding-left: $euiSizeS; + padding-right: $euiSizeS; + } + } + + &__deleteIconContainer { + /* + * Create a bit of space between the timeline and the delete icon + */ + padding: $euiSizeM; + margin-left: $euiSizeM; + background-color: $ilmDeletePhaseBackgroundColor; + color: $ilmDeletePhaseColor; + border-radius: calc(#{$euiSizeS} / 2); + } + + &__colorBar { + display: inline-block; + height: $ilmTimelineBarHeight; + border-radius: calc(#{$ilmTimelineBarHeight} / 2); + width: 100%; + } + + &__hotPhase { + width: var(--ilm-timeline-hot-phase-width); + + &__colorBar { + background-color: $euiColorVis9; + } + } + + &__warmPhase { + width: var(--ilm-timeline-warm-phase-width); + + &__colorBar { + background-color: $euiColorVis5; + } + } + + &__coldPhase { + width: var(--ilm-timeline-cold-phase-width); + + &__colorBar { + background-color: $euiColorVis1; + } + } +} diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx new file mode 100644 index 0000000000000..40bab9c676de2 --- /dev/null +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/components/timeline/timeline.tsx @@ -0,0 +1,208 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import React, { FunctionComponent, useMemo } from 'react'; +import { + EuiText, + EuiIcon, + EuiIconProps, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiIconTip, +} from '@elastic/eui'; + +import { PhasesExceptDelete } from '../../../../../../common/types'; +import { useFormData } from '../../../../../shared_imports'; + +import { FormInternal } from '../../types'; + +import { + calculateRelativeTimingMs, + normalizeTimingsToHumanReadable, + PhaseAgeInMilliseconds, +} from '../../lib'; + +import './timeline.scss'; +import { InfinityIconSvg } from './infinity_icon.svg'; + +const InfinityIcon: FunctionComponent> = (props) => ( + +); + +const toPercent = (n: number, total: number) => (n / total) * 100; + +const msTimeToOverallPercent = (ms: number, totalMs: number) => { + if (!isFinite(ms)) { + return 100; + } + if (totalMs === 0) { + return 100; + } + return toPercent(ms, totalMs); +}; + +/** + * Each phase, if active, should have a minimum width it occupies. The higher this + * base amount, the smaller the variance in phase size in the timeline. This functions + * as a min-width constraint. + */ +const SCORE_BUFFER_AMOUNT = 50; + +const i18nTexts = { + hotPhase: i18n.translate('xpack.indexLifecycleMgmt.timeline.hotPhaseSectionTitle', { + defaultMessage: 'Hot phase', + }), + warmPhase: i18n.translate('xpack.indexLifecycleMgmt.timeline.warmPhaseSectionTitle', { + defaultMessage: 'Warm phase', + }), + coldPhase: i18n.translate('xpack.indexLifecycleMgmt.timeline.coldPhaseSectionTitle', { + defaultMessage: 'Cold phase', + }), + deleteIcon: { + toolTipContent: i18n.translate('xpack.indexLifecycleMgmt.timeline.deleteIconToolTipContent', { + defaultMessage: 'Policy deletes the index after lifecycle phases complete.', + }), + }, +}; + +const calculateWidths = (inputs: PhaseAgeInMilliseconds) => { + const hotScore = msTimeToOverallPercent(inputs.phases.hot, inputs.total) + SCORE_BUFFER_AMOUNT; + const warmScore = + inputs.phases.warm != null + ? msTimeToOverallPercent(inputs.phases.warm, inputs.total) + SCORE_BUFFER_AMOUNT + : 0; + const coldScore = + inputs.phases.cold != null + ? msTimeToOverallPercent(inputs.phases.cold, inputs.total) + SCORE_BUFFER_AMOUNT + : 0; + + const totalScore = hotScore + warmScore + coldScore; + return { + hot: `${toPercent(hotScore, totalScore)}%`, + warm: `${toPercent(warmScore, totalScore)}%`, + cold: `${toPercent(coldScore, totalScore)}%`, + }; +}; + +const TimelinePhaseText: FunctionComponent<{ + phaseName: string; + durationInPhase?: React.ReactNode | string; +}> = ({ phaseName, durationInPhase }) => ( + + + + {phaseName} + + + + {typeof durationInPhase === 'string' ? ( + {durationInPhase} + ) : ( + durationInPhase + )} + + +); + +export const Timeline: FunctionComponent = () => { + const [formData] = useFormData(); + + const phaseTimingInMs = useMemo(() => { + return calculateRelativeTimingMs(formData); + }, [formData]); + + const humanReadableTimings = useMemo(() => normalizeTimingsToHumanReadable(phaseTimingInMs), [ + phaseTimingInMs, + ]); + + const widths = calculateWidths(phaseTimingInMs); + + const getDurationInPhaseContent = (phase: PhasesExceptDelete): string | React.ReactNode => + phaseTimingInMs.phases[phase] === Infinity ? ( + + ) : ( + humanReadableTimings[phase] + ); + + return ( + + + +

+ {i18n.translate('xpack.indexLifecycleMgmt.timeline.title', { + defaultMessage: 'Policy Timeline', + })} +

+
+
+ +
{ + if (el) { + el.style.setProperty('--ilm-timeline-hot-phase-width', widths.hot); + el.style.setProperty('--ilm-timeline-warm-phase-width', widths.warm ?? null); + el.style.setProperty('--ilm-timeline-cold-phase-width', widths.cold ?? null); + } + }} + > + + +
+ {/* These are the actual color bars for the timeline */} +
+
+ +
+ {formData._meta?.warm.enabled && ( +
+
+ +
+ )} + {formData._meta?.cold.enabled && ( +
+
+ +
+ )} +
+ + {formData._meta?.delete.enabled && ( + +
+ +
+
+ )} + +
+ + + ); +}; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx index d945ae8bb3e4e..228a0f9fdb942 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx +++ b/x-pack/plugins/index_lifecycle_management/public/application/sections/edit_policy/edit_policy.tsx @@ -43,6 +43,7 @@ import { DeletePhase, HotPhase, WarmPhase, + Timeline, } from './components'; import { schema, deserializer, createSerializer, createPolicyNameValidations, Form } from './form'; @@ -256,7 +257,11 @@ export const EditPolicy: React.FunctionComponent = ({ history }) => { ) : null} - + + + + + From 1e7b6f0ec25974641f3d8e96f5257ba8c518d515 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 26 Jan 2021 13:42:27 -0800 Subject: [PATCH 66/90] skip flaky suite (#88826) --- test/functional/apps/home/_navigation.ts | 80 +++++++++++++----------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index c90398fa84afc..90d13b4b5e417 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -15,42 +15,46 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const appsMenu = getService('appsMenu'); const esArchiver = getService('esArchiver'); - describe('Kibana browser back navigation should work', function describeIndexTests() { - before(async () => { - await esArchiver.loadIfNeeded('discover'); - await esArchiver.loadIfNeeded('logstash_functional'); - }); - - it('detect navigate back issues', async () => { - let currUrl; - // Detects bug described in issue #31238 - where back navigation would get stuck to URL encoding handling in Angular. - // Navigate to home app - await PageObjects.common.navigateToApp('home'); - const homeUrl = await browser.getCurrentUrl(); - - // Navigate to discover app - await appsMenu.clickLink('Discover'); - const discoverUrl = await browser.getCurrentUrl(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); - - // Navigate to dashboard app - await appsMenu.clickLink('Dashboard'); - - // Navigating back to discover - await browser.goBack(); - currUrl = await browser.getCurrentUrl(); - expect(currUrl).to.be(modifiedTimeDiscoverUrl); - - // Navigating back from time settings - await browser.goBack(); // undo time settings - currUrl = await browser.getCurrentUrl(); - expect(currUrl.startsWith(discoverUrl)).to.be(true); - - // Navigate back home - await browser.goBack(); - currUrl = await browser.getCurrentUrl(); - expect(currUrl).to.be(homeUrl); - }); - }); + // Failing: See https://github.com/elastic/kibana/issues/88826 + describe.skip( + 'Kibana browser back navigation should work', + function describeIndexTests() { + before(async () => { + await esArchiver.loadIfNeeded('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + }); + + it('detect navigate back issues', async () => { + let currUrl; + // Detects bug described in issue #31238 - where back navigation would get stuck to URL encoding handling in Angular. + // Navigate to home app + await PageObjects.common.navigateToApp('home'); + const homeUrl = await browser.getCurrentUrl(); + + // Navigate to discover app + await appsMenu.clickLink('Discover'); + const discoverUrl = await browser.getCurrentUrl(); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); + + // Navigate to dashboard app + await appsMenu.clickLink('Dashboard'); + + // Navigating back to discover + await browser.goBack(); + currUrl = await browser.getCurrentUrl(); + expect(currUrl).to.be(modifiedTimeDiscoverUrl); + + // Navigating back from time settings + await browser.goBack(); // undo time settings + currUrl = await browser.getCurrentUrl(); + expect(currUrl.startsWith(discoverUrl)).to.be(true); + + // Navigate back home + await browser.goBack(); + currUrl = await browser.getCurrentUrl(); + expect(currUrl).to.be(homeUrl); + }); + } + ); } From 2a9a9305ee4cf6b79b917ff6c8d76575af739aff Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 26 Jan 2021 14:14:08 -0800 Subject: [PATCH 67/90] Fixes linting caused by skipping flaky suite Signed-off-by: Tyler Smalley --- test/functional/apps/home/_navigation.ts | 79 ++++++++++++------------ 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/test/functional/apps/home/_navigation.ts b/test/functional/apps/home/_navigation.ts index 90d13b4b5e417..12f97a5349419 100644 --- a/test/functional/apps/home/_navigation.ts +++ b/test/functional/apps/home/_navigation.ts @@ -16,45 +16,42 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); // Failing: See https://github.com/elastic/kibana/issues/88826 - describe.skip( - 'Kibana browser back navigation should work', - function describeIndexTests() { - before(async () => { - await esArchiver.loadIfNeeded('discover'); - await esArchiver.loadIfNeeded('logstash_functional'); - }); - - it('detect navigate back issues', async () => { - let currUrl; - // Detects bug described in issue #31238 - where back navigation would get stuck to URL encoding handling in Angular. - // Navigate to home app - await PageObjects.common.navigateToApp('home'); - const homeUrl = await browser.getCurrentUrl(); - - // Navigate to discover app - await appsMenu.clickLink('Discover'); - const discoverUrl = await browser.getCurrentUrl(); - await PageObjects.timePicker.setDefaultAbsoluteRange(); - const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); - - // Navigate to dashboard app - await appsMenu.clickLink('Dashboard'); - - // Navigating back to discover - await browser.goBack(); - currUrl = await browser.getCurrentUrl(); - expect(currUrl).to.be(modifiedTimeDiscoverUrl); - - // Navigating back from time settings - await browser.goBack(); // undo time settings - currUrl = await browser.getCurrentUrl(); - expect(currUrl.startsWith(discoverUrl)).to.be(true); - - // Navigate back home - await browser.goBack(); - currUrl = await browser.getCurrentUrl(); - expect(currUrl).to.be(homeUrl); - }); - } - ); + describe.skip('Kibana browser back navigation should work', function describeIndexTests() { + before(async () => { + await esArchiver.loadIfNeeded('discover'); + await esArchiver.loadIfNeeded('logstash_functional'); + }); + + it('detect navigate back issues', async () => { + let currUrl; + // Detects bug described in issue #31238 - where back navigation would get stuck to URL encoding handling in Angular. + // Navigate to home app + await PageObjects.common.navigateToApp('home'); + const homeUrl = await browser.getCurrentUrl(); + + // Navigate to discover app + await appsMenu.clickLink('Discover'); + const discoverUrl = await browser.getCurrentUrl(); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + const modifiedTimeDiscoverUrl = await browser.getCurrentUrl(); + + // Navigate to dashboard app + await appsMenu.clickLink('Dashboard'); + + // Navigating back to discover + await browser.goBack(); + currUrl = await browser.getCurrentUrl(); + expect(currUrl).to.be(modifiedTimeDiscoverUrl); + + // Navigating back from time settings + await browser.goBack(); // undo time settings + currUrl = await browser.getCurrentUrl(); + expect(currUrl.startsWith(discoverUrl)).to.be(true); + + // Navigate back home + await browser.goBack(); + currUrl = await browser.getCurrentUrl(); + expect(currUrl).to.be(homeUrl); + }); + }); } From 20f32d506fd72f567eda678c90a67a4ff9e75f2c Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 26 Jan 2021 14:20:42 -0800 Subject: [PATCH 68/90] JSON Body payload for the webhook connector in Alerts & Actions (#89253) * fixes https://github.com/elastic/kibana/issues/74449 Co-authored-by: Patrick Mueller --- docs/user/alerting/action-types/webhook.asciidoc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/user/alerting/action-types/webhook.asciidoc b/docs/user/alerting/action-types/webhook.asciidoc index 659c3afad6bd1..fff6814325ea4 100644 --- a/docs/user/alerting/action-types/webhook.asciidoc +++ b/docs/user/alerting/action-types/webhook.asciidoc @@ -72,4 +72,17 @@ Password:: An optional password. If set, HTTP basic authentication is used. Cur Webhook actions have the following properties: -Body:: A json payload sent to the request URL. +Body:: A JSON payload sent to the request URL. For example: ++ +[source,text] +-- +{ + "short_description": "{{context.rule.name}}", + "description": "{{context.rule.description}}", + ... +} +-- + +Mustache template variables (the text enclosed in double braces, for example, `context.rule.name`) have +their values escaped, so that the final JSON will be valid (escaping double quote characters). +For more information on Mustache template variables, refer to <>. From b5b9cee1584997a480d6a2a2b5edafcf3fcf9976 Mon Sep 17 00:00:00 2001 From: Justin Ibarra Date: Tue, 26 Jan 2021 13:43:05 -0900 Subject: [PATCH 69/90] Update security solution generic timeline templates (#89239) * Update security solution generic timeline templates --- .../rules/prepackaged_timelines/endpoint.json | 2 +- .../rules/prepackaged_timelines/index.ndjson | 6 +++--- .../rules/prepackaged_timelines/network.json | 2 +- .../rules/prepackaged_timelines/process.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/endpoint.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/endpoint.json index 711050e1f136a..acc5f69358798 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/endpoint.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/endpoint.json @@ -1 +1 @@ -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":"{agent.type}","field":"agent.type","displayField":"agent.type","value":"{agent.type}","operator":":*"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"","queryMatch":{"displayValue":"endpoint","field":"agent.type","displayField":"agent.type","value":"endpoint","operator":":"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"default","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Endpoint Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"db366523-f1c6-4c1f-8731-6ce5ed9e5717","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735857110,"createdBy":"Elastic","updated":1594736314036,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"","queryMatch":{"displayValue":"endpoint","field":"agent.type","displayField":"agent.type","value":"endpoint","operator":":"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"default","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Endpoint Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"db366523-f1c6-4c1f-8731-6ce5ed9e5717","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735857110,"createdBy":"Elastic","updated":1611609999115,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/index.ndjson b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/index.ndjson index 7c074242c39d1..522430205c25a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/index.ndjson +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/index.ndjson @@ -7,6 +7,6 @@ // Auto generated file from scripts/regen_prepackage_timelines_index.sh // Do not hand edit. Run that script to regenerate package information instead -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":"{agent.type}","field":"agent.type","displayField":"agent.type","value":"{agent.type}","operator":":*"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"","queryMatch":{"displayValue":"endpoint","field":"agent.type","displayField":"agent.type","value":"endpoint","operator":":"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"default","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Endpoint Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"db366523-f1c6-4c1f-8731-6ce5ed9e5717","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735857110,"createdBy":"Elastic","updated":1594736314036,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.","columnHeaderType":"not-filtered","id":"event.action","category":"event","type":"string","searchable":null,"example":"user-password-change"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the source (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"source.ip","category":"source","type":"ip","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Port of the source.","columnHeaderType":"not-filtered","id":"source.port","category":"source","type":"number","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the destination (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"destination.ip","category":"destination","type":"ip","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"destination.port","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"host.name","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{source.ip}","queryMatch":{"displayValue":null,"field":"source.ip","displayField":null,"value":"{source.ip}","operator":":*"},"id":"timeline-1-ec778f01-1802-40f0-9dfb-ed8de1f656cb","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"{destination.ip}","queryMatch":{"displayValue":null,"field":"destination.ip","displayField":null,"value":"{destination.ip}","operator":":*"},"id":"timeline-1-e37e37c5-a6e7-4338-af30-47bfbc3c0e1e","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Network Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"91832785-286d-4ebe-b884-1a208d111a70","dateRange":{"start":1588255858373,"end":1588256218373},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735573866,"createdBy":"Elastic","updated":1594736099397,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":null,"field":"process.name","displayField":null,"value":"{process.name}","operator":":"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"{event.type}","queryMatch":{"displayValue":null,"field":"event.type","displayField":null,"value":"{event.type}","operator":":*"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Process Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"76e52245-7519-4251-91ab-262fb1a1728c","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735629389,"createdBy":"Elastic","updated":1594736083598,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"","queryMatch":{"displayValue":"endpoint","field":"agent.type","displayField":"agent.type","value":"endpoint","operator":":"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"default","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Endpoint Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"db366523-f1c6-4c1f-8731-6ce5ed9e5717","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735857110,"createdBy":"Elastic","updated":1611609999115,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.","columnHeaderType":"not-filtered","id":"event.action","category":"event","type":"string","searchable":null,"example":"user-password-change"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the source (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"source.ip","category":"source","type":"ip","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Port of the source.","columnHeaderType":"not-filtered","id":"source.port","category":"source","type":"number","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the destination (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"destination.ip","category":"destination","type":"ip","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"destination.port","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"host.name","searchable":null}],"dataProviders":[{"and":[{"enabled":true,"excluded":false,"id":"timeline-1-e37e37c5-a6e7-4338-af30-47bfbc3c0e1e","kqlQuery":"","name":"{destination.ip}","queryMatch":{"displayField":"destination.ip","displayValue":"{destination.ip}","field":"destination.ip","operator":":","value":"{destination.ip}"},"type":"template"}],"enabled":true,"excluded":false,"id":"timeline-1-ec778f01-1802-40f0-9dfb-ed8de1f656cb","kqlQuery":"","name":"{source.ip}","queryMatch":{"displayField":"source.ip","displayValue":"{source.ip}","field":"source.ip","operator":":","value":"{source.ip}"},"type":"template"}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Network Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"91832785-286d-4ebe-b884-1a208d111a70","dateRange":{"start":1588255858373,"end":1588256218373},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735573866,"createdBy":"Elastic","updated":1611609960850,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":null,"field":"process.name","displayField":null,"value":"{process.name}","operator":":"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Process Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"76e52245-7519-4251-91ab-262fb1a1728c","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735629389,"createdBy":"Elastic","updated":1611609848602,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/network.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/network.json index 1634476b4e99d..6e93387579d22 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/network.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/network.json @@ -1 +1 @@ -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.","columnHeaderType":"not-filtered","id":"event.action","category":"event","type":"string","searchable":null,"example":"user-password-change"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the source (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"source.ip","category":"source","type":"ip","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Port of the source.","columnHeaderType":"not-filtered","id":"source.port","category":"source","type":"number","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the destination (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"destination.ip","category":"destination","type":"ip","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"destination.port","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"host.name","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{source.ip}","queryMatch":{"displayValue":null,"field":"source.ip","displayField":null,"value":"{source.ip}","operator":":*"},"id":"timeline-1-ec778f01-1802-40f0-9dfb-ed8de1f656cb","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"{destination.ip}","queryMatch":{"displayValue":null,"field":"destination.ip","displayField":null,"value":"{destination.ip}","operator":":*"},"id":"timeline-1-e37e37c5-a6e7-4338-af30-47bfbc3c0e1e","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Network Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"91832785-286d-4ebe-b884-1a208d111a70","dateRange":{"start":1588255858373,"end":1588256218373},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735573866,"createdBy":"Elastic","updated":1594736099397,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The action captured by the event.\n\nThis describes the information in the event. It is more specific than `event.category`.\nExamples are `group-add`, `process-started`, `file-created`. The value is\nnormally defined by the implementer.","columnHeaderType":"not-filtered","id":"event.action","category":"event","type":"string","searchable":null,"example":"user-password-change"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the source (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"source.ip","category":"source","type":"ip","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Port of the source.","columnHeaderType":"not-filtered","id":"source.port","category":"source","type":"number","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"IP address of the destination (IPv4 or IPv6).","columnHeaderType":"not-filtered","id":"destination.ip","category":"destination","type":"ip","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"destination.port","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"host.name","searchable":null}],"dataProviders":[{"and":[{"enabled":true,"excluded":false,"id":"timeline-1-e37e37c5-a6e7-4338-af30-47bfbc3c0e1e","kqlQuery":"","name":"{destination.ip}","queryMatch":{"displayField":"destination.ip","displayValue":"{destination.ip}","field":"destination.ip","operator":":","value":"{destination.ip}"},"type":"template"}],"enabled":true,"excluded":false,"id":"timeline-1-ec778f01-1802-40f0-9dfb-ed8de1f656cb","kqlQuery":"","name":"{source.ip}","queryMatch":{"displayField":"source.ip","displayValue":"{source.ip}","field":"source.ip","operator":":","value":"{source.ip}"},"type":"template"}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Network Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"91832785-286d-4ebe-b884-1a208d111a70","dateRange":{"start":1588255858373,"end":1588256218373},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735573866,"createdBy":"Elastic","updated":1611609960850,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/process.json b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/process.json index 767f38133f263..c25873746a9e9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/process.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_timelines/process.json @@ -1 +1 @@ -{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":null,"field":"process.name","displayField":null,"value":"{process.name}","operator":":"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true},{"excluded":false,"and":[],"kqlQuery":"","name":"{event.type}","queryMatch":{"displayValue":null,"field":"event.type","displayField":null,"value":"{event.type}","operator":":*"},"id":"timeline-1-4685da24-35c1-43f3-892d-1f926dbf5568","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Process Timeline","timelineType":"template","templateTimelineVersion":1,"templateTimelineId":"76e52245-7519-4251-91ab-262fb1a1728c","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735629389,"createdBy":"Elastic","updated":1594736083598,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} +{"savedObjectId":null,"version":null,"columns":[{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"@timestamp","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"signal.rule.description","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"event.action","searchable":null},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.name","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"The working directory of the process.","columnHeaderType":"not-filtered","id":"process.working_directory","category":"process","type":"string","searchable":null,"example":"/home/alice"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments, starting with the absolute path to\nthe executable.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.args","category":"process","type":"string","searchable":null,"example":"[\"/usr/bin/ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"name":null,"columnHeaderType":"not-filtered","id":"process.pid","searchable":null},{"indexes":null,"aggregatable":true,"name":null,"description":"Absolute path to the process executable.","columnHeaderType":"not-filtered","id":"process.parent.executable","category":"process","type":"string","searchable":null,"example":"/usr/bin/ssh"},{"indexes":null,"aggregatable":true,"name":null,"description":"Array of process arguments.\n\nMay be filtered to protect sensitive information.","columnHeaderType":"not-filtered","id":"process.parent.args","category":"process","type":"string","searchable":null,"example":"[\"ssh\",\"-l\",\"user\",\"10.0.0.16\"]"},{"indexes":null,"aggregatable":true,"name":null,"description":"Process id.","columnHeaderType":"not-filtered","id":"process.parent.pid","category":"process","type":"number","searchable":null,"example":"4242"},{"indexes":null,"aggregatable":true,"name":null,"description":"Short name or login of the user.","columnHeaderType":"not-filtered","id":"user.name","category":"user","type":"string","searchable":null,"example":"albert"},{"indexes":null,"aggregatable":true,"name":null,"description":"Name of the host.\n\nIt can contain what `hostname` returns on Unix systems, the fully qualified\ndomain name, or a name specified by the user. The sender decides which value\nto use.","columnHeaderType":"not-filtered","id":"host.name","category":"host","type":"string","searchable":null}],"dataProviders":[{"excluded":false,"and":[],"kqlQuery":"","name":"{process.name}","queryMatch":{"displayValue":null,"field":"process.name","displayField":null,"value":"{process.name}","operator":":"},"id":"timeline-1-8622010a-61fb-490d-b162-beac9c36a853","type":"template","enabled":true}],"description":"","eventType":"all","filters":[],"kqlMode":"filter","kqlQuery":{"filterQuery":{"kuery":{"kind":"kuery","expression":""},"serializedQuery":""}},"title":"Generic Process Timeline","timelineType":"template","templateTimelineVersion":2,"templateTimelineId":"76e52245-7519-4251-91ab-262fb1a1728c","dateRange":{"start":1588161020848,"end":1588162280848},"savedQueryId":null,"sort":{"columnId":"@timestamp","sortDirection":"desc"},"created":1594735629389,"createdBy":"Elastic","updated":1611609848602,"updatedBy":"Elastic","eventNotes":[],"globalNotes":[],"pinnedEventIds":[],"status":"immutable"} From 0fe7b9e080c67c43aefdb7ea25d5e90a80cb4ade Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 26 Jan 2021 15:27:48 -0800 Subject: [PATCH 70/90] skip flaky suite (#89031) --- test/functional/apps/management/_test_huge_fields.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/management/_test_huge_fields.js b/test/functional/apps/management/_test_huge_fields.js index ca95af8cb4205..2ab619276d2b9 100644 --- a/test/functional/apps/management/_test_huge_fields.js +++ b/test/functional/apps/management/_test_huge_fields.js @@ -13,7 +13,8 @@ export default function ({ getService, getPageObjects }) { const security = getService('security'); const PageObjects = getPageObjects(['common', 'home', 'settings']); - describe('test large number of fields', function () { + // Failing: See https://github.com/elastic/kibana/issues/89031 + describe.skip('test large number of fields', function () { this.tags(['skipCloud']); const EXPECTED_FIELD_COUNT = '10006'; From 2c648acffda7c05cac2949eb5994c0f2066aa12b Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 26 Jan 2021 16:30:52 -0800 Subject: [PATCH 71/90] skip flaky suite (#89379) --- test/functional/apps/home/_sample_data.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/home/_sample_data.ts b/test/functional/apps/home/_sample_data.ts index a9fe2026112b6..438dd6f8adce2 100644 --- a/test/functional/apps/home/_sample_data.ts +++ b/test/functional/apps/home/_sample_data.ts @@ -20,7 +20,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const dashboardExpect = getService('dashboardExpect'); const PageObjects = getPageObjects(['common', 'header', 'home', 'dashboard', 'timePicker']); - describe('sample data', function describeIndexTests() { + // Failing: See https://github.com/elastic/kibana/issues/89379 + describe.skip('sample data', function describeIndexTests() { before(async () => { await security.testUser.setRoles(['kibana_admin', 'kibana_sample_admin']); await PageObjects.common.navigateToUrl('home', '/tutorial_directory/sampleData', { From 8250b078b4b47415435ee72fb9b04908f99c6ed5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Jan 2021 00:59:24 +0000 Subject: [PATCH 72/90] chore(NA): improve ts build refs performance on kbn bootstrap (#89333) * chore(NA): improve ts build refs performance on kbn bootstrap * chore(NA): use skipLibCheck=false by default on typechecking * docs(NA): commit core docs changes * fix(NA): eui typings --- ...lugin-core-server.kibanaresponsefactory.md | 8 +- ...ns-data-public.aggconfigs._constructor_.md | 6 +- ...na-plugin-plugins-data-public.searchbar.md | 4 +- ...plugin-plugins-data-server.plugin.start.md | 4 +- package.json | 10 +- src/core/server/server.api.md | 8 +- src/dev/typescript/run_type_check_cli.ts | 6 +- src/plugins/data/public/public.api.md | 8 +- src/plugins/data/server/server.api.md | 2 +- tsconfig.base.json | 2 + typings/@elastic/eui/index.d.ts | 10 +- typings/@elastic/eui/lib/format.d.ts | 9 -- typings/@elastic/eui/lib/services.d.ts | 9 -- yarn.lock | 119 +++++++++--------- 14 files changed, 99 insertions(+), 106 deletions(-) delete mode 100644 typings/@elastic/eui/lib/format.d.ts delete mode 100644 typings/@elastic/eui/lib/services.d.ts diff --git a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md index b488baacaff25..d7eafdce017e4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md +++ b/docs/development/core/server/kibana-plugin-core-server.kibanaresponsefactory.md @@ -10,7 +10,7 @@ Set of helpers used to create `KibanaResponse` to form HTTP response on an incom ```typescript kibanaResponseFactory: { - custom: | { + custom: | Buffer | Error | Stream | { message: string | Error; attributes?: Record | undefined; } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; @@ -21,9 +21,9 @@ kibanaResponseFactory: { conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; internalError: (options?: ErrorHttpResponseOptions) => KibanaResponse; customError: (options: CustomHttpResponseOptions) => KibanaResponse; - redirected: (options: RedirectResponseOptions) => KibanaResponse>; - ok: (options?: HttpResponseOptions) => KibanaResponse>; - accepted: (options?: HttpResponseOptions) => KibanaResponse>; + redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; + ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; noContent: (options?: HttpResponseOptions) => KibanaResponse; } ``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md index c9e08b9712480..6ca7a1a88b30e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggconfigs._constructor_.md @@ -15,11 +15,11 @@ constructor(indexPattern: IndexPattern, configStates: Pick & Pick<{ + }, "schema" | "enabled" | "id" | "params"> & Pick<{ type: string | IAggType; }, "type"> & Pick<{ type: string | IAggType; - }, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined, opts: AggConfigsOptions); + }, never>, "schema" | "type" | "enabled" | "id" | "params">[] | undefined, opts: AggConfigsOptions); ``` ## Parameters @@ -27,6 +27,6 @@ constructor(indexPattern: IndexPattern, configStates: PickIndexPattern | | -| configStates | Pick<Pick<{
type: string;
enabled?: boolean | undefined;
id?: string | undefined;
params?: {} | import("./agg_config").SerializableState | undefined;
schema?: string | undefined;
}, "enabled" | "schema" | "id" | "params"> & Pick<{
type: string | IAggType;
}, "type"> & Pick<{
type: string | IAggType;
}, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined | | +| configStates | Pick<Pick<{
type: string;
enabled?: boolean | undefined;
id?: string | undefined;
params?: {} | import("./agg_config").SerializableState | undefined;
schema?: string | undefined;
}, "schema" | "enabled" | "id" | "params"> & Pick<{
type: string | IAggType;
}, "type"> & Pick<{
type: string | IAggType;
}, never>, "schema" | "type" | "enabled" | "id" | "params">[] | undefined | | | opts | AggConfigsOptions | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md index 2fd84730957b6..83fbc00860ca5 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchbar.md @@ -7,7 +7,7 @@ Signature: ```typescript -SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; } ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md index af7abb076d7ef..ea3ba28a52def 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.plugin.start.md @@ -12,7 +12,7 @@ start(core: CoreStart): { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }; @@ -31,7 +31,7 @@ start(core: CoreStart): { fieldFormatServiceFactory: (uiSettings: import("../../../core/server").IUiSettingsClient) => Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }` diff --git a/package.json b/package.json index d14c5b0a7dc5f..42949a7014131 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/request": "^2.88.2", "**/trim": "0.0.3", - "**/typescript": "4.1.2" + "**/typescript": "4.1.3" }, "engines": { "node": "14.15.4", @@ -561,8 +561,8 @@ "@types/xml2js": "^0.4.5", "@types/yauzl": "^2.9.1", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^4.8.1", - "@typescript-eslint/parser": "^4.8.1", + "@typescript-eslint/eslint-plugin": "^4.14.1", + "@typescript-eslint/parser": "^4.14.1", "@welldone-software/why-did-you-render": "^5.0.0", "@yarnpkg/lockfile": "^1.1.0", "abab": "^2.0.4", @@ -820,9 +820,9 @@ "topojson-client": "3.0.0", "ts-loader": "^7.0.5", "tsd": "^0.13.1", - "typescript": "4.1.2", + "typescript": "4.1.3", "typescript-fsa": "^3.0.0", - "typescript-fsa-reducers": "^1.2.1", + "typescript-fsa-reducers": "^1.2.2", "unlazy-loader": "^0.1.3", "unstated": "^2.1.1", "url-loader": "^2.2.0", diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index ceab69a6cdb18..0b058011267eb 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1256,7 +1256,7 @@ export type KibanaResponseFactory = typeof kibanaResponseFactory; // @public export const kibanaResponseFactory: { - custom: | { + custom: | Buffer | Error | Stream | { message: string | Error; attributes?: Record | undefined; } | undefined>(options: CustomHttpResponseOptions) => KibanaResponse; @@ -1267,9 +1267,9 @@ export const kibanaResponseFactory: { conflict: (options?: ErrorHttpResponseOptions) => KibanaResponse; internalError: (options?: ErrorHttpResponseOptions) => KibanaResponse; customError: (options: CustomHttpResponseOptions) => KibanaResponse; - redirected: (options: RedirectResponseOptions) => KibanaResponse>; - ok: (options?: HttpResponseOptions) => KibanaResponse>; - accepted: (options?: HttpResponseOptions) => KibanaResponse>; + redirected: (options: RedirectResponseOptions) => KibanaResponse | Buffer | Stream>; + ok: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; + accepted: (options?: HttpResponseOptions) => KibanaResponse | Buffer | Stream>; noContent: (options?: HttpResponseOptions) => KibanaResponse; }; diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index c1a25a8e4bdee..6e7ea6fcf2405 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -61,7 +61,7 @@ export async function runTypeCheckCli() { Options: --project [path] {dim Path to a tsconfig.json file determines the project to check} - --skip-lib-check {dim Skip type checking of all declaration files (*.d.ts)} + --skip-lib-check {dim Skip type checking of all declaration files (*.d.ts). Default is false} --help {dim Show this message} `) ); @@ -77,7 +77,9 @@ export async function runTypeCheckCli() { ...['--emitDeclarationOnly', 'false'], '--noEmit', '--pretty', - ...(opts['skip-lib-check'] ? ['--skipLibCheck'] : []), + ...(opts['skip-lib-check'] + ? ['--skipLibCheck', opts['skip-lib-check']] + : ['--skipLibCheck', 'false']), ]; const projects = filterProjectsByFlag(opts.project).filter((p) => !p.disableTypeCheck); diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 28997de4517e7..002f033365790 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -218,11 +218,11 @@ export class AggConfigs { id?: string | undefined; params?: {} | import("./agg_config").SerializableState | undefined; schema?: string | undefined; - }, "enabled" | "schema" | "id" | "params"> & Pick<{ + }, "schema" | "enabled" | "id" | "params"> & Pick<{ type: string | IAggType; }, "type"> & Pick<{ type: string | IAggType; - }, never>, "enabled" | "type" | "schema" | "id" | "params">[] | undefined, opts: AggConfigsOptions); + }, never>, "schema" | "type" | "enabled" | "id" | "params">[] | undefined, opts: AggConfigsOptions); // (undocumented) aggs: IAggConfig[]; // (undocumented) @@ -2235,8 +2235,8 @@ export const search: { // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: React.ComponentClass, "query" | "isLoading" | "filters" | "indexPatterns" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { - WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; +export const SearchBar: React.ComponentClass, "query" | "isLoading" | "indexPatterns" | "filters" | "dataTestSubj" | "refreshInterval" | "screenTitle" | "onRefresh" | "onRefreshChange" | "showQueryInput" | "showDatePicker" | "showAutoRefreshOnly" | "dateRangeFrom" | "dateRangeTo" | "isRefreshPaused" | "customSubmitButton" | "timeHistory" | "indicateNoData" | "onFiltersUpdated" | "trackUiMetric" | "savedQuery" | "showSaveQuery" | "onClearSavedQuery" | "showQueryBar" | "showFilterBar" | "onQueryChange" | "onQuerySubmit" | "onSaved" | "onSavedQueryUpdated">, any> & { + WrappedComponent: React.ComponentType & ReactIntl.InjectedIntlProps>; }; // Warning: (ae-forgotten-export) The symbol "SearchBarOwnProps" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 6a96fd8209a8d..9789f3354e9ef 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -1138,7 +1138,7 @@ export class Plugin implements Plugin_2 Promise; }; indexPatterns: { - indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; + indexPatternsServiceFactory: (savedObjectsClient: Pick, elasticsearchClient: import("../../../core/server").ElasticsearchClient) => Promise; }; search: ISearchStart>; }; diff --git a/tsconfig.base.json b/tsconfig.base.json index 247813da51cfb..f8e07911e71ce 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -14,6 +14,8 @@ "strict": true, // save information about the project graph on disk "incremental": true, + // Do not check d.ts files by default + "skipLibCheck": true, // enables "core language features" "lib": [ "esnext", diff --git a/typings/@elastic/eui/index.d.ts b/typings/@elastic/eui/index.d.ts index 74f89608bc04f..e5cf7864a83b2 100644 --- a/typings/@elastic/eui/index.d.ts +++ b/typings/@elastic/eui/index.d.ts @@ -6,6 +6,12 @@ * Public License, v 1. */ -import { Direction } from '@elastic/eui/src/services/sort/sort_direction'; - // TODO: Remove once typescript definitions are in EUI + +declare module '@elastic/eui/lib/services' { + export const RIGHT_ALIGNMENT: any; +} + +declare module '@elastic/eui/lib/services/format' { + export const dateFormatAliases: any; +} diff --git a/typings/@elastic/eui/lib/format.d.ts b/typings/@elastic/eui/lib/format.d.ts deleted file mode 100644 index 4be830ec2d98c..0000000000000 --- a/typings/@elastic/eui/lib/format.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export const dateFormatAliases: any; diff --git a/typings/@elastic/eui/lib/services.d.ts b/typings/@elastic/eui/lib/services.d.ts deleted file mode 100644 index a667d111ceb72..0000000000000 --- a/typings/@elastic/eui/lib/services.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export const RIGHT_ALIGNMENT: any; diff --git a/yarn.lock b/yarn.lock index 174f1284a3a6b..ed861b58773b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6861,28 +6861,29 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== -"@typescript-eslint/eslint-plugin@^4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.1.tgz#b362abe0ee478a6c6d06c14552a6497f0b480769" - integrity sha512-d7LeQ7dbUrIv5YVFNzGgaW3IQKMmnmKFneRWagRlGYOSfLJVaRbj/FrBNOBC1a3tVO+TgNq1GbHvRtg1kwL0FQ== +"@typescript-eslint/eslint-plugin@^4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.14.1.tgz#22dd301ce228aaab3416b14ead10b1db3e7d3180" + integrity sha512-5JriGbYhtqMS1kRcZTQxndz1lKMwwEXKbwZbkUZNnp6MJX0+OVXnG0kOlBZP4LUAxEyzu3cs+EXd/97MJXsGfw== dependencies: - "@typescript-eslint/experimental-utils" "4.8.1" - "@typescript-eslint/scope-manager" "4.8.1" + "@typescript-eslint/experimental-utils" "4.14.1" + "@typescript-eslint/scope-manager" "4.14.1" debug "^4.1.1" functional-red-black-tree "^1.0.1" + lodash "^4.17.15" regexpp "^3.0.0" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.8.1.tgz#27275c20fa4336df99ebcf6195f7d7aa7aa9f22d" - integrity sha512-WigyLn144R3+lGATXW4nNcDJ9JlTkG8YdBWHkDlN0lC3gUGtDi7Pe3h5GPvFKMcRz8KbZpm9FJV9NTW8CpRHpg== +"@typescript-eslint/experimental-utils@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.14.1.tgz#a5c945cb24dabb96747180e1cfc8487f8066f471" + integrity sha512-2CuHWOJwvpw0LofbyG5gvYjEyoJeSvVH2PnfUQSn0KQr4v8Dql2pr43ohmx4fdPQ/eVoTSFjTi/bsGEXl/zUUQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.8.1" - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/typescript-estree" "4.8.1" + "@typescript-eslint/scope-manager" "4.14.1" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/typescript-estree" "4.14.1" eslint-scope "^5.0.0" eslint-utils "^2.0.0" @@ -6898,16 +6899,24 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.8.1.tgz#4fe2fbdbb67485bafc4320b3ae91e34efe1219d1" - integrity sha512-QND8XSVetATHK9y2Ltc/XBl5Ro7Y62YuZKnPEwnNPB8E379fDsvzJ1dMJ46fg/VOmk0hXhatc+GXs5MaXuL5Uw== +"@typescript-eslint/parser@^4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.14.1.tgz#3bd6c24710cd557d8446625284bcc9c6d52817c6" + integrity sha512-mL3+gU18g9JPsHZuKMZ8Z0Ss9YP1S5xYZ7n68Z98GnPq02pYNQuRXL85b9GYhl6jpdvUc45Km7hAl71vybjUmw== dependencies: - "@typescript-eslint/scope-manager" "4.8.1" - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/typescript-estree" "4.8.1" + "@typescript-eslint/scope-manager" "4.14.1" + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/typescript-estree" "4.14.1" debug "^4.1.1" +"@typescript-eslint/scope-manager@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.14.1.tgz#8444534254c6f370e9aa974f035ced7fe713ce02" + integrity sha512-F4bjJcSqXqHnC9JGUlnqSa3fC2YH5zTtmACS1Hk+WX/nFB0guuynVK5ev35D4XZbdKjulXBAQMyRr216kmxghw== + dependencies: + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/visitor-keys" "4.14.1" + "@typescript-eslint/scope-manager@4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.3.0.tgz#c743227e087545968080d2362cfb1273842cb6a7" @@ -6916,23 +6925,29 @@ "@typescript-eslint/types" "4.3.0" "@typescript-eslint/visitor-keys" "4.3.0" -"@typescript-eslint/scope-manager@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.8.1.tgz#e343c475f8f1d15801b546cb17d7f309b768fdce" - integrity sha512-r0iUOc41KFFbZdPAdCS4K1mXivnSZqXS5D9oW+iykQsRlTbQRfuFRSW20xKDdYiaCoH+SkSLeIF484g3kWzwOQ== - dependencies: - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/visitor-keys" "4.8.1" +"@typescript-eslint/types@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.14.1.tgz#b3d2eb91dafd0fd8b3fce7c61512ac66bd0364aa" + integrity sha512-SkhzHdI/AllAgQSxXM89XwS1Tkic7csPdndUuTKabEwRcEfR8uQ/iPA3Dgio1rqsV3jtqZhY0QQni8rLswJM2w== "@typescript-eslint/types@4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.3.0.tgz#1f0b2d5e140543e2614f06d48fb3ae95193c6ddf" integrity sha512-Cx9TpRvlRjOppGsU6Y6KcJnUDOelja2NNCX6AZwtVHRzaJkdytJWMuYiqi8mS35MRNA3cJSwDzXePfmhU6TANw== -"@typescript-eslint/types@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.8.1.tgz#23829c73c5fc6f4fcd5346a7780b274f72fee222" - integrity sha512-ave2a18x2Y25q5K05K/U3JQIe2Av4+TNi/2YuzyaXLAsDx6UZkz1boZ7nR/N6Wwae2PpudTZmHFXqu7faXfHmA== +"@typescript-eslint/typescript-estree@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.14.1.tgz#20d3b8c8e3cdc8f764bdd5e5b0606dd83da6075b" + integrity sha512-M8+7MbzKC1PvJIA8kR2sSBnex8bsR5auatLCnVlNTJczmJgqRn8M+sAlQfkEq7M4IY3WmaNJ+LJjPVRrREVSHQ== + dependencies: + "@typescript-eslint/types" "4.14.1" + "@typescript-eslint/visitor-keys" "4.14.1" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" "@typescript-eslint/typescript-estree@4.3.0": version "4.3.0" @@ -6948,19 +6963,13 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/typescript-estree@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.8.1.tgz#7307e3f2c9e95df7daa8dc0a34b8c43b7ec0dd32" - integrity sha512-bJ6Fn/6tW2g7WIkCWh3QRlaSU7CdUUK52shx36/J7T5oTQzANvi6raoTsbwGM11+7eBbeem8hCCKbyvAc0X3sQ== +"@typescript-eslint/visitor-keys@4.14.1": + version "4.14.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.14.1.tgz#e93c2ff27f47ee477a929b970ca89d60a117da91" + integrity sha512-TAblbDXOI7bd0C/9PE1G+AFo7R5uc+ty1ArDoxmrC1ah61Hn6shURKy7gLdRb1qKJmjHkqu5Oq+e4Kt0jwf1IA== dependencies: - "@typescript-eslint/types" "4.8.1" - "@typescript-eslint/visitor-keys" "4.8.1" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/types" "4.14.1" + eslint-visitor-keys "^2.0.0" "@typescript-eslint/visitor-keys@4.3.0": version "4.3.0" @@ -6970,14 +6979,6 @@ "@typescript-eslint/types" "4.3.0" eslint-visitor-keys "^2.0.0" -"@typescript-eslint/visitor-keys@4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.8.1.tgz#794f68ee292d1b2e3aa9690ebedfcb3a8c90e3c3" - integrity sha512-3nrwXFdEYALQh/zW8rFwP4QltqsanCDz4CwWMPiIZmwlk9GlvBeueEIbq05SEq4ganqM0g9nh02xXgv5XI3PeQ== - dependencies: - "@typescript-eslint/types" "4.8.1" - eslint-visitor-keys "^2.0.0" - "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -28571,10 +28572,10 @@ typescript-compare@^0.0.2: dependencies: typescript-logic "^0.0.0" -typescript-fsa-reducers@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/typescript-fsa-reducers/-/typescript-fsa-reducers-1.2.1.tgz#2af1a85f7b88fb0dfb9fa59d5da51a5d7ac6543f" - integrity sha512-Qgn7zEnAU5n3YEWEL5ooEmIWZl9B4QyXD4Y/0DqpUzF0YuTrcsLa7Lht0gFXZ+xqLJXQwo3fEiTfQTDF1fBnMg== +typescript-fsa-reducers@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/typescript-fsa-reducers/-/typescript-fsa-reducers-1.2.2.tgz#1e2c8e8a50ca3e09e0d6fdf2a3b42bdd17de8094" + integrity sha512-IQ2VsIqUvmzVgWNDjxkeOxX97itl/rq+2u82jGsRdzCSFi9OtV4qf1Ec1urvj/eDlPHOaihIL7wMZzLYx9GvFg== typescript-fsa@^3.0.0: version "3.0.0" @@ -28593,10 +28594,10 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.1.2, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.5.3, typescript@~3.7.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" - integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== +typescript@4.1.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.5.3, typescript@~3.7.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== ua-parser-js@^0.7.18: version "0.7.22" From c6cfdee5b02d30263972bf149601323883a8c351 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 26 Jan 2021 20:23:01 -0700 Subject: [PATCH 73/90] [core.logging] Add ops logs to the KP logging system (#88070) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../src/get_logging_config.ts | 5 +- src/core/README.md | 6 +- .../deprecation/core_deprecations.test.ts | 20 +++ .../config/deprecation/core_deprecations.ts | 12 ++ src/core/server/logging/README.md | 24 +++- src/core/server/logging/ecs.ts | 90 ++++++++++++ src/core/server/logging/index.ts | 7 + .../logging/get_ops_metrics_log.test.ts | 132 ++++++++++++++++++ .../metrics/logging/get_ops_metrics_log.ts | 78 +++++++++++ src/core/server/metrics/logging/index.ts | 9 ++ .../server/metrics/metrics_service.test.ts | 99 ++++++++++++- src/core/server/metrics/metrics_service.ts | 6 +- 12 files changed, 482 insertions(+), 6 deletions(-) create mode 100644 src/core/server/logging/ecs.ts create mode 100644 src/core/server/metrics/logging/get_ops_metrics_log.test.ts create mode 100644 src/core/server/metrics/logging/get_ops_metrics_log.ts create mode 100644 src/core/server/metrics/logging/index.ts diff --git a/packages/kbn-legacy-logging/src/get_logging_config.ts b/packages/kbn-legacy-logging/src/get_logging_config.ts index 5a28ab1271538..280b9f815c78c 100644 --- a/packages/kbn-legacy-logging/src/get_logging_config.ts +++ b/packages/kbn-legacy-logging/src/get_logging_config.ts @@ -28,7 +28,10 @@ export function getLoggingConfiguration(config: LegacyLoggingConfig, opsInterval } else if (config.verbose) { _.defaults(events, { log: '*', - ops: '*', + // To avoid duplicate logs, we explicitly disable this in verbose + // mode as it is already provided by the new logging config under + // the `metrics.ops` context. + ops: '!', request: '*', response: '*', error: '*', diff --git a/src/core/README.md b/src/core/README.md index e195bf30c054c..c73c6aa56bfd0 100644 --- a/src/core/README.md +++ b/src/core/README.md @@ -28,8 +28,10 @@ Even though the new validation system provided by the `core` is also based on Jo rules tailored to our needs (e.g. `byteSize`, `duration` etc.). That means that config values that were previously accepted by the "legacy" Kibana may be rejected by the `core` now. -Even though `core` has its own logging system it doesn't output log records directly (e.g. to file or terminal), but instead -forward them to the "legacy" Kibana so that they look the same as the rest of the log records throughout Kibana. +### Logging +`core` has its own [logging system](./server/logging/README.md) and will output log records directly (e.g. to file or terminal) when configured. When no +specific configuration is provided, logs are forwarded to the "legacy" Kibana so that they look the same as the rest of the +log records throughout Kibana. ## Core API Review To provide a stable API for plugin developers, it is important that the Core Public and Server API's are stable and diff --git a/src/core/server/config/deprecation/core_deprecations.test.ts b/src/core/server/config/deprecation/core_deprecations.test.ts index a7c6a63826523..a791362d9166f 100644 --- a/src/core/server/config/deprecation/core_deprecations.test.ts +++ b/src/core/server/config/deprecation/core_deprecations.test.ts @@ -236,4 +236,24 @@ describe('core deprecations', () => { ).toEqual([`worker-src blob:`]); }); }); + + describe('logging.events.ops', () => { + it('warns when ops events are used', () => { + const { messages } = applyCoreDeprecations({ + logging: { events: { ops: '*' } }, + }); + expect(messages).toMatchInlineSnapshot(` + Array [ + "\\"logging.events.ops\\" has been deprecated and will be removed in 8.0. To access ops data moving forward, please enable debug logs for the \\"metrics.ops\\" context in your logging configuration.", + ] + `); + }); + + it('does not warn when other events are configured', () => { + const { messages } = applyCoreDeprecations({ + logging: { events: { log: '*' } }, + }); + expect(messages).toEqual([]); + }); + }); }); diff --git a/src/core/server/config/deprecation/core_deprecations.ts b/src/core/server/config/deprecation/core_deprecations.ts index 6f6e6c3e0522e..23a3518cd8eb6 100644 --- a/src/core/server/config/deprecation/core_deprecations.ts +++ b/src/core/server/config/deprecation/core_deprecations.ts @@ -103,6 +103,17 @@ const mapManifestServiceUrlDeprecation: ConfigDeprecation = (settings, fromPath, return settings; }; +const opsLoggingEventDeprecation: ConfigDeprecation = (settings, fromPath, log) => { + if (has(settings, 'logging.events.ops')) { + log( + '"logging.events.ops" has been deprecated and will be removed ' + + 'in 8.0. To access ops data moving forward, please enable debug logs for the ' + + '"metrics.ops" context in your logging configuration.' + ); + } + return settings; +}; + export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unusedFromRoot }) => [ unusedFromRoot('savedObjects.indexCheckTimeout'), unusedFromRoot('server.xsrf.token'), @@ -137,4 +148,5 @@ export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unu rewriteBasePathDeprecation, cspRulesDeprecation, mapManifestServiceUrlDeprecation, + opsLoggingEventDeprecation, ]; diff --git a/src/core/server/logging/README.md b/src/core/server/logging/README.md index 8cb704f09ce8c..cc2b6230d2d33 100644 --- a/src/core/server/logging/README.md +++ b/src/core/server/logging/README.md @@ -312,6 +312,9 @@ logging: - context: telemetry level: all appenders: [json-file-appender] + - context: metrics.ops + level: debug + appenders: [console] ``` Here is what we get with the config above: @@ -324,6 +327,7 @@ Here is what we get with the config above: | server | console, file | fatal | | optimize | console | error | | telemetry | json-file-appender | all | +| metrics.ops | console | debug | The `root` logger has a dedicated configuration node since this context is special and should always exist. By @@ -341,7 +345,25 @@ Or disable logging entirely with `off`: ```yaml logging.root.level: off ``` +### Dedicated loggers + +The `metrics.ops` logger is configured with `debug` level and will automatically output sample system and process information at a regular interval. +The metrics that are logged are a subset of the data collected and are formatted in the log message as follows: + +| Ops formatted log property | Location in metrics service | Log units +| :------------------------- | :-------------------------- | :-------------------------- | +| memory | process.memory.heap.used_in_bytes | [depends on the value](http://numeraljs.com/#format), typically MB or GB | +| uptime | process.uptime_in_millis | HH:mm:ss | +| load | os.load | [ "load for the last 1 min" "load for the last 5 min" "load for the last 15 min"] | +| delay | process.event_loop_delay | ms | + +The log interval is the same as the interval at which system and process information is refreshed and is configurable under `ops.interval`: + +```yaml +ops.interval: 5000 +``` +The minimum interval is 100ms and defaults to 5000ms. ## Usage Usage is very straightforward, one should just get a logger for a specific context and use it to log messages with @@ -478,4 +500,4 @@ TBD | meta | separate property `"meta": {"to": "v8"}` | merged in log record `{... "to": "v8"}` | | pid | `pid: 12345` | `pid: 12345` | | type | N/A | `type: log` | -| error | `{ message, name, stack }` | `{ message, name, stack, code, signal }` | \ No newline at end of file +| error | `{ message, name, stack }` | `{ message, name, stack, code, signal }` | diff --git a/src/core/server/logging/ecs.ts b/src/core/server/logging/ecs.ts new file mode 100644 index 0000000000000..0dbc403fca0b2 --- /dev/null +++ b/src/core/server/logging/ecs.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +/** + * Typings for some ECS fields which core uses internally. + * These are not a complete set of ECS typings and should not + * be used externally; the only types included here are ones + * currently used in core. + * + * @internal + */ + +export interface EcsOpsMetricsEvent { + /** + * These typings were written as of ECS 1.7.0. + * Don't change this value without checking the rest + * of the types to conform to that ECS version. + * + * https://www.elastic.co/guide/en/ecs/1.7/index.html + */ + ecs: { version: '1.7.0' }; + + // base fields + ['@timestamp']?: string; + labels?: Record; + message?: string; + tags?: string[]; + // other fields + process?: EcsProcessField; + event?: EcsEventField; +} + +interface EcsProcessField { + uptime?: number; +} + +export interface EcsEventField { + kind?: EcsEventKind; + category?: EcsEventCategory[]; + type?: EcsEventType; +} + +export enum EcsEventKind { + ALERT = 'alert', + EVENT = 'event', + METRIC = 'metric', + STATE = 'state', + PIPELINE_ERROR = 'pipeline_error', + SIGNAL = 'signal', +} + +export enum EcsEventCategory { + AUTHENTICATION = 'authentication', + CONFIGURATION = 'configuration', + DATABASE = 'database', + DRIVER = 'driver', + FILE = 'file', + HOST = 'host', + IAM = 'iam', + INTRUSION_DETECTION = 'intrusion_detection', + MALWARE = 'malware', + NETWORK = 'network', + PACKAGE = 'package', + PROCESS = 'process', + WEB = 'web', +} + +export enum EcsEventType { + ACCESS = 'access', + ADMIN = 'admin', + ALLOWED = 'allowed', + CHANGE = 'change', + CONNECTION = 'connection', + CREATION = 'creation', + DELETION = 'deletion', + DENIED = 'denied', + END = 'end', + ERROR = 'error', + GROUP = 'group', + INFO = 'info', + INSTALLATION = 'installation', + PROTOCOL = 'protocol', + START = 'start', + USER = 'user', +} diff --git a/src/core/server/logging/index.ts b/src/core/server/logging/index.ts index f024bea1bf358..18a903af0a9fd 100644 --- a/src/core/server/logging/index.ts +++ b/src/core/server/logging/index.ts @@ -17,6 +17,13 @@ export { LogLevelId, LogLevel, } from '@kbn/logging'; +export { + EcsOpsMetricsEvent, + EcsEventField, + EcsEventKind, + EcsEventCategory, + EcsEventType, +} from './ecs'; export { config, LoggingConfigType, diff --git a/src/core/server/metrics/logging/get_ops_metrics_log.test.ts b/src/core/server/metrics/logging/get_ops_metrics_log.test.ts new file mode 100644 index 0000000000000..820959910e764 --- /dev/null +++ b/src/core/server/metrics/logging/get_ops_metrics_log.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { OpsMetrics } from '..'; +import { getEcsOpsMetricsLog } from './get_ops_metrics_log'; + +function createBaseOpsMetrics(): OpsMetrics { + return { + collected_at: new Date('2020-01-01 01:00:00'), + process: { + memory: { + heap: { total_in_bytes: 1, used_in_bytes: 1, size_limit: 1 }, + resident_set_size_in_bytes: 1, + }, + event_loop_delay: 1, + pid: 1, + uptime_in_millis: 1, + }, + os: { + platform: 'darwin' as const, + platformRelease: 'test', + load: { '1m': 1, '5m': 1, '15m': 1 }, + memory: { total_in_bytes: 1, free_in_bytes: 1, used_in_bytes: 1 }, + uptime_in_millis: 1, + }, + response_times: { avg_in_millis: 1, max_in_millis: 1 }, + requests: { disconnects: 1, total: 1, statusCodes: { '200': 1 } }, + concurrent_connections: 1, + }; +} + +function createMockOpsMetrics(testMetrics: Partial): OpsMetrics { + const base = createBaseOpsMetrics(); + return { + ...base, + ...testMetrics, + }; +} +const testMetrics = ({ + process: { + memory: { heap: { used_in_bytes: 100 } }, + uptime_in_millis: 1500, + event_loop_delay: 50, + }, + os: { + load: { + '1m': 10, + '5m': 20, + '15m': 30, + }, + }, +} as unknown) as Partial; + +describe('getEcsOpsMetricsLog', () => { + it('provides correctly formatted message', () => { + const result = getEcsOpsMetricsLog(createMockOpsMetrics(testMetrics)); + expect(result.message).toMatchInlineSnapshot( + `"memory: 100.0B uptime: 0:00:01 load: [10.00,20.00,30.00] delay: 50.000"` + ); + }); + + it('correctly formats process uptime', () => { + const logMeta = getEcsOpsMetricsLog(createMockOpsMetrics(testMetrics)); + expect(logMeta.process!.uptime).toEqual(1); + }); + + it('excludes values from the message if unavailable', () => { + const baseMetrics = createBaseOpsMetrics(); + const missingMetrics = ({ + ...baseMetrics, + process: {}, + os: {}, + } as unknown) as OpsMetrics; + const logMeta = getEcsOpsMetricsLog(missingMetrics); + expect(logMeta.message).toMatchInlineSnapshot(`""`); + }); + + it('specifies correct ECS version', () => { + const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics()); + expect(logMeta.ecs.version).toBe('1.7.0'); + }); + + it('provides an ECS-compatible response', () => { + const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics()); + expect(logMeta).toMatchInlineSnapshot(` + Object { + "ecs": Object { + "version": "1.7.0", + }, + "event": Object { + "category": Array [ + "process", + "host", + ], + "kind": "metric", + "type": "info", + }, + "host": Object { + "os": Object { + "load": Object { + "15m": 1, + "1m": 1, + "5m": 1, + }, + }, + }, + "message": "memory: 1.0B load: [1.00,1.00,1.00] delay: 1.000", + "process": Object { + "eventLoopDelay": 1, + "memory": Object { + "heap": Object { + "usedInBytes": 1, + }, + }, + "uptime": 0, + }, + } + `); + }); + + it('logs ECS fields in the log meta', () => { + const logMeta = getEcsOpsMetricsLog(createBaseOpsMetrics()); + expect(logMeta.event!.kind).toBe('metric'); + expect(logMeta.event!.category).toEqual(expect.arrayContaining(['process', 'host'])); + expect(logMeta.event!.type).toBe('info'); + }); +}); diff --git a/src/core/server/metrics/logging/get_ops_metrics_log.ts b/src/core/server/metrics/logging/get_ops_metrics_log.ts new file mode 100644 index 0000000000000..361cac0bc310c --- /dev/null +++ b/src/core/server/metrics/logging/get_ops_metrics_log.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import numeral from '@elastic/numeral'; +import { EcsOpsMetricsEvent, EcsEventKind, EcsEventCategory, EcsEventType } from '../../logging'; +import { OpsMetrics } from '..'; + +const ECS_VERSION = '1.7.0'; +/** + * Converts ops metrics into ECS-compliant `LogMeta` for logging + * + * @internal + */ +export function getEcsOpsMetricsLog(metrics: OpsMetrics): EcsOpsMetricsEvent { + const { process, os } = metrics; + const processMemoryUsedInBytes = process?.memory?.heap?.used_in_bytes; + const processMemoryUsedInBytesMsg = processMemoryUsedInBytes + ? `memory: ${numeral(processMemoryUsedInBytes).format('0.0b')} ` + : ''; + + // ECS process.uptime is in seconds: + const uptimeVal = process?.uptime_in_millis + ? Math.floor(process.uptime_in_millis / 1000) + : undefined; + + // HH:mm:ss message format for backward compatibility + const uptimeValMsg = uptimeVal ? `uptime: ${numeral(uptimeVal).format('00:00:00')} ` : ''; + + // Event loop delay is in ms + const eventLoopDelayVal = process?.event_loop_delay; + const eventLoopDelayValMsg = eventLoopDelayVal + ? `delay: ${numeral(process?.event_loop_delay).format('0.000')}` + : ''; + + const loadEntries = { + '1m': os?.load ? os?.load['1m'] : undefined, + '5m': os?.load ? os?.load['5m'] : undefined, + '15m': os?.load ? os?.load['15m'] : undefined, + }; + + const loadVals = [...Object.values(os?.load ?? [])]; + const loadValsMsg = + loadVals.length > 0 + ? `load: [${loadVals.map((val: number) => { + return numeral(val).format('0.00'); + })}] ` + : ''; + + return { + ecs: { version: ECS_VERSION }, + message: `${processMemoryUsedInBytesMsg}${uptimeValMsg}${loadValsMsg}${eventLoopDelayValMsg}`, + event: { + kind: EcsEventKind.METRIC, + category: [EcsEventCategory.PROCESS, EcsEventCategory.HOST], + type: EcsEventType.INFO, + }, + process: { + uptime: uptimeVal, + // @ts-expect-error custom fields not yet part of ECS + memory: { + heap: { + usedInBytes: processMemoryUsedInBytes, + }, + }, + eventLoopDelay: eventLoopDelayVal, + }, + host: { + os: { + load: loadEntries, + }, + }, + }; +} diff --git a/src/core/server/metrics/logging/index.ts b/src/core/server/metrics/logging/index.ts new file mode 100644 index 0000000000000..5b3f9aed56be0 --- /dev/null +++ b/src/core/server/metrics/logging/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export { getEcsOpsMetricsLog } from './get_ops_metrics_log'; diff --git a/src/core/server/metrics/metrics_service.test.ts b/src/core/server/metrics/metrics_service.test.ts index 5ba29606d8a80..e21bad1ef4be7 100644 --- a/src/core/server/metrics/metrics_service.test.ts +++ b/src/core/server/metrics/metrics_service.test.ts @@ -13,12 +13,15 @@ import { mockOpsCollector } from './metrics_service.test.mocks'; import { MetricsService } from './metrics_service'; import { mockCoreContext } from '../core_context.mock'; import { httpServiceMock } from '../http/http_service.mock'; +import { loggingSystemMock } from '../logging/logging_system.mock'; import { take } from 'rxjs/operators'; const testInterval = 100; const dummyMetrics = { metricA: 'value', metricB: 'otherValue' }; +const logger = loggingSystemMock.create(); + describe('MetricsService', () => { const httpMock = httpServiceMock.createInternalSetupContract(); let metricsService: MetricsService; @@ -29,7 +32,7 @@ describe('MetricsService', () => { const configService = configServiceMock.create({ atPath: { interval: moment.duration(testInterval) }, }); - const coreContext = mockCoreContext.create({ configService }); + const coreContext = mockCoreContext.create({ logger, configService }); metricsService = new MetricsService(coreContext); }); @@ -118,6 +121,100 @@ describe('MetricsService', () => { expect(await nextEmission()).toEqual({ metric: 'first' }); expect(await nextEmission()).toEqual({ metric: 'second' }); }); + + it('logs the metrics at every interval', async () => { + const firstMetrics = { + process: { + memory: { heap: { used_in_bytes: 100 } }, + uptime_in_millis: 1500, + event_loop_delay: 50, + }, + os: { + load: { + '1m': 10, + '5m': 20, + '15m': 30, + }, + }, + }; + const secondMetrics = { + process: { + memory: { heap: { used_in_bytes: 200 } }, + uptime_in_millis: 3000, + event_loop_delay: 100, + }, + os: { + load: { + '1m': 20, + '5m': 30, + '15m': 40, + }, + }, + }; + + const opsLogger = logger.get('metrics', 'ops'); + + mockOpsCollector.collect + .mockResolvedValueOnce(firstMetrics) + .mockResolvedValueOnce(secondMetrics); + await metricsService.setup({ http: httpMock }); + const { getOpsMetrics$ } = await metricsService.start(); + + const nextEmission = async () => { + jest.advanceTimersByTime(testInterval); + const emission = await getOpsMetrics$().pipe(take(1)).toPromise(); + await new Promise((resolve) => process.nextTick(resolve)); + return emission; + }; + + await nextEmission(); + const opsLogs = loggingSystemMock.collect(opsLogger).debug; + expect(opsLogs.length).toEqual(2); + expect(opsLogs[0][1]).not.toEqual(opsLogs[1][1]); + }); + + it('omits metrics from log message if they are missing or malformed', async () => { + const opsLogger = logger.get('metrics', 'ops'); + mockOpsCollector.collect.mockResolvedValueOnce({ secondMetrics: 'metrics' }); + await metricsService.setup({ http: httpMock }); + await metricsService.start(); + expect(loggingSystemMock.collect(opsLogger).debug[0]).toMatchInlineSnapshot(` + Array [ + "", + Object { + "ecs": Object { + "version": "1.7.0", + }, + "event": Object { + "category": Array [ + "process", + "host", + ], + "kind": "metric", + "type": "info", + }, + "host": Object { + "os": Object { + "load": Object { + "15m": undefined, + "1m": undefined, + "5m": undefined, + }, + }, + }, + "process": Object { + "eventLoopDelay": undefined, + "memory": Object { + "heap": Object { + "usedInBytes": undefined, + }, + }, + "uptime": undefined, + }, + }, + ] + `); + }); }); describe('#stop', () => { diff --git a/src/core/server/metrics/metrics_service.ts b/src/core/server/metrics/metrics_service.ts index 24b2b1b67a07a..460035ad2e298 100644 --- a/src/core/server/metrics/metrics_service.ts +++ b/src/core/server/metrics/metrics_service.ts @@ -15,6 +15,7 @@ import { InternalHttpServiceSetup } from '../http'; import { InternalMetricsServiceSetup, InternalMetricsServiceStart, OpsMetrics } from './types'; import { OpsMetricsCollector } from './ops_metrics_collector'; import { opsConfig, OpsConfigType } from './ops_config'; +import { getEcsOpsMetricsLog } from './logging'; interface MetricsServiceSetupDeps { http: InternalHttpServiceSetup; @@ -24,6 +25,7 @@ interface MetricsServiceSetupDeps { export class MetricsService implements CoreService { private readonly logger: Logger; + private readonly opsMetricsLogger: Logger; private metricsCollector?: OpsMetricsCollector; private collectInterval?: NodeJS.Timeout; private metrics$ = new ReplaySubject(1); @@ -31,6 +33,7 @@ export class MetricsService constructor(private readonly coreContext: CoreContext) { this.logger = coreContext.logger.get('metrics'); + this.opsMetricsLogger = coreContext.logger.get('metrics', 'ops'); } public async setup({ http }: MetricsServiceSetupDeps): Promise { @@ -69,8 +72,9 @@ export class MetricsService } private async refreshMetrics() { - this.logger.debug('Refreshing metrics'); const metrics = await this.metricsCollector!.collect(); + const { message, ...meta } = getEcsOpsMetricsLog(metrics); + this.opsMetricsLogger.debug(message!, meta); this.metricsCollector!.reset(); this.metrics$.next(metrics); } From 3fe2e95e350ce8d13272df633d97aa2eabc62a07 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 26 Jan 2021 22:53:14 -0500 Subject: [PATCH 74/90] Rename conversion function, extract to module scope and add tests. (#89018) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/requests/get_network_events.test.ts | 12 +++++++++++- .../uptime/server/lib/requests/get_network_events.ts | 12 +++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts index 2d590e80ca42d..e1c62f3469518 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.test.ts @@ -5,9 +5,19 @@ */ import { getUptimeESMockClient } from './helper'; -import { getNetworkEvents } from './get_network_events'; +import { getNetworkEvents, secondsToMillis } from './get_network_events'; describe('getNetworkEvents', () => { + describe('secondsToMillis conversion', () => { + it('returns -1 for -1 value', () => { + expect(secondsToMillis(-1)).toBe(-1); + }); + + it('returns a value of seconds as milliseconds', () => { + expect(secondsToMillis(10)).toBe(10_000); + }); + }); + let mockHits: any; beforeEach(() => { diff --git a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts index ec1fffd62350d..6b1bca8f2d7b7 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_network_events.ts @@ -12,6 +12,10 @@ interface GetNetworkEventsParams { stepIndex: string; } +export const secondsToMillis = (seconds: number) => + // -1 is a special case where a value was unavailable + seconds === -1 ? -1 : seconds * 1000; + export const getNetworkEvents: UMElasticsearchQueryFn< GetNetworkEventsParams, { events: NetworkEvent[]; total: number } @@ -35,17 +39,15 @@ export const getNetworkEvents: UMElasticsearchQueryFn< const { body: result } = await uptimeEsClient.search({ body: params }); - const microToMillis = (micro: number): number => (micro === -1 ? -1 : micro * 1000); - return { total: result.hits.total.value, events: result.hits.hits.map((event: any) => { - const requestSentTime = microToMillis(event._source.synthetics.payload.request_sent_time); - const loadEndTime = microToMillis(event._source.synthetics.payload.load_end_time); + const requestSentTime = secondsToMillis(event._source.synthetics.payload.request_sent_time); + const loadEndTime = secondsToMillis(event._source.synthetics.payload.load_end_time); const requestStartTime = event._source.synthetics.payload.response && event._source.synthetics.payload.response.timing - ? microToMillis(event._source.synthetics.payload.response.timing.request_time) + ? secondsToMillis(event._source.synthetics.payload.response.timing.request_time) : undefined; return { From 053628cae862103b8c27bc60656bf45d76dc3f5d Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 27 Jan 2021 09:54:56 +0100 Subject: [PATCH 75/90] [APM] Optimize API test order (#88654) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fake_mocha_types.d.ts | 2 + .../snapshots/decorate_snapshot_ui.test.ts | 63 ++- .../lib/snapshots/decorate_snapshot_ui.ts | 89 ++-- .../test/apm_api_integration/basic/config.ts | 8 +- .../basic/tests/alerts/chart_preview.ts | 124 ----- .../apm_api_integration/basic/tests/index.ts | 72 --- .../tests/metrics_charts/metrics_charts.ts | 439 ---------------- .../basic/tests/service_maps/service_maps.ts | 32 -- .../basic/tests/services/annotations.ts | 48 -- .../basic/tests/services/throughput.ts | 85 --- .../basic/tests/services/top_services.ts | 259 --------- .../tests/settings/agent_configuration.ts | 489 ----------------- .../anomaly_detection/no_access_user.ts | 43 -- .../settings/anomaly_detection/read_user.ts | 46 -- .../settings/anomaly_detection/write_user.ts | 50 -- .../basic/tests/settings/custom_link.ts | 35 -- .../basic/tests/transactions/breakdown.ts | 123 ----- .../basic/tests/transactions/latency.ts | 102 ---- .../test/apm_api_integration/common/config.ts | 17 +- .../apm_api_integration/common/registry.ts | 160 ++++++ .../test/apm_api_integration/configs/index.ts | 25 + .../tests/alerts/chart_preview.ts | 70 +++ .../tests/correlations/slow_transactions.ts | 88 ++++ .../csm/__snapshots__/page_load_dist.snap | 8 +- .../tests/csm/__snapshots__/page_views.snap | 8 +- .../tests/csm/csm_services.ts | 40 ++ .../{trial => }/tests/csm/has_rum_data.ts | 39 +- .../{trial => }/tests/csm/js_errors.ts | 39 +- .../tests/csm/long_task_metrics.ts | 47 +- .../tests/csm/page_load_dist.ts | 58 ++ .../tests/csm/page_views.ts | 58 ++ .../tests/csm/url_search.ts | 82 +++ .../{trial => }/tests/csm/web_core_vitals.ts | 51 +- .../{basic => }/tests/feature_controls.ts | 5 +- .../test/apm_api_integration/tests/index.ts | 70 +++ .../tests/metrics_charts/metrics_charts.ts | 446 ++++++++++++++++ .../tests/observability_overview/has_data.ts | 37 +- .../observability_overview.ts | 42 +- .../__snapshots__/service_maps.snap | 6 +- .../tests/service_maps/service_maps.ts | 155 +++--- .../service_overview/dependencies/es_utils.ts | 0 .../service_overview/dependencies/index.ts | 44 +- .../tests/service_overview/error_groups.ts | 27 +- .../tests/service_overview/instances.ts | 65 +-- .../services/__snapshots__/throughput.snap | 2 +- .../{basic => }/tests/services/agent_name.ts | 35 +- .../{trial => }/tests/services/annotations.ts | 30 +- .../tests/services/service_details.ts | 27 +- .../tests/services/service_icons.ts | 63 ++- .../tests/services/throughput.ts | 82 +++ .../tests/services/top_services.ts | 364 +++++++++++++ .../tests/services/transaction_types.ts | 27 +- .../tests/settings/agent_configuration.ts | 495 ++++++++++++++++++ .../tests/settings/anomaly_detection/basic.ts | 53 ++ .../anomaly_detection/no_access_user.ts | 44 ++ .../settings/anomaly_detection/read_user.ts | 46 ++ .../settings/anomaly_detection/write_user.ts | 56 ++ .../tests/settings/custom_link.ts | 183 +++++++ .../traces/__snapshots__/top_traces.snap | 2 +- .../{basic => }/tests/traces/top_traces.ts | 36 +- .../transactions/__snapshots__/breakdown.snap | 4 +- .../__snapshots__/error_rate.snap | 2 +- .../transactions/__snapshots__/latency.snap | 46 ++ .../__snapshots__/top_transaction_groups.snap | 2 +- .../__snapshots__/transaction_charts.snap | 0 .../__snapshots__/transactions_charts.snap | 0 .../tests/transactions/breakdown.ts | 118 +++++ .../tests/transactions/distribution.ts | 26 +- .../tests/transactions/error_rate.ts | 43 +- .../tests/transactions/latency.ts | 243 +++++++++ .../tests/transactions/throughput.ts | 53 +- .../transactions/top_transaction_groups.ts | 26 +- .../transactions_groups_overview.ts | 27 +- .../test/apm_api_integration/trial/config.ts | 8 +- .../tests/correlations/slow_transactions.ts | 95 ---- .../trial/tests/csm/csm_services.ts | 47 -- .../trial/tests/csm/page_load_dist.ts | 64 --- .../trial/tests/csm/page_views.ts | 64 --- .../trial/tests/csm/url_search.ts | 89 ---- .../apm_api_integration/trial/tests/index.ts | 50 -- .../trial/tests/services/top_services.ts | 131 ----- .../anomaly_detection/no_access_user.ts | 41 -- .../settings/anomaly_detection/read_user.ts | 43 -- .../settings/anomaly_detection/write_user.ts | 53 -- .../trial/tests/settings/custom_link.ts | 159 ------ .../transactions/__snapshots__/latency.snap | 46 -- .../trial/tests/transactions/latency.ts | 159 ------ 87 files changed, 3447 insertions(+), 3533 deletions(-) delete mode 100644 x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/index.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/services/annotations.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/services/throughput.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/services/top_services.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transactions/breakdown.ts delete mode 100644 x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts create mode 100644 x-pack/test/apm_api_integration/common/registry.ts create mode 100644 x-pack/test/apm_api_integration/configs/index.ts create mode 100644 x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts create mode 100644 x-pack/test/apm_api_integration/tests/correlations/slow_transactions.ts rename x-pack/test/apm_api_integration/{trial => }/tests/csm/__snapshots__/page_load_dist.snap (95%) rename x-pack/test/apm_api_integration/{trial => }/tests/csm/__snapshots__/page_views.snap (90%) create mode 100644 x-pack/test/apm_api_integration/tests/csm/csm_services.ts rename x-pack/test/apm_api_integration/{trial => }/tests/csm/has_rum_data.ts (53%) rename x-pack/test/apm_api_integration/{trial => }/tests/csm/js_errors.ts (72%) rename x-pack/test/apm_api_integration/{trial => }/tests/csm/long_task_metrics.ts (50%) create mode 100644 x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts create mode 100644 x-pack/test/apm_api_integration/tests/csm/page_views.ts create mode 100644 x-pack/test/apm_api_integration/tests/csm/url_search.ts rename x-pack/test/apm_api_integration/{trial => }/tests/csm/web_core_vitals.ts (55%) rename x-pack/test/apm_api_integration/{basic => }/tests/feature_controls.ts (98%) create mode 100644 x-pack/test/apm_api_integration/tests/index.ts create mode 100644 x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts rename x-pack/test/apm_api_integration/{basic => }/tests/observability_overview/has_data.ts (67%) rename x-pack/test/apm_api_integration/{basic => }/tests/observability_overview/observability_overview.ts (69%) rename x-pack/test/apm_api_integration/{trial => }/tests/service_maps/__snapshots__/service_maps.snap (99%) rename x-pack/test/apm_api_integration/{trial => }/tests/service_maps/service_maps.ts (58%) rename x-pack/test/apm_api_integration/{basic => }/tests/service_overview/dependencies/es_utils.ts (100%) rename x-pack/test/apm_api_integration/{basic => }/tests/service_overview/dependencies/index.ts (91%) rename x-pack/test/apm_api_integration/{basic => }/tests/service_overview/error_groups.ts (94%) rename x-pack/test/apm_api_integration/{basic => }/tests/service_overview/instances.ts (83%) rename x-pack/test/apm_api_integration/{basic => }/tests/services/__snapshots__/throughput.snap (96%) rename x-pack/test/apm_api_integration/{basic => }/tests/services/agent_name.ts (55%) rename x-pack/test/apm_api_integration/{trial => }/tests/services/annotations.ts (92%) rename x-pack/test/apm_api_integration/{basic => }/tests/services/service_details.ts (87%) rename x-pack/test/apm_api_integration/{basic => }/tests/services/service_icons.ts (53%) create mode 100644 x-pack/test/apm_api_integration/tests/services/throughput.ts create mode 100644 x-pack/test/apm_api_integration/tests/services/top_services.ts rename x-pack/test/apm_api_integration/{basic => }/tests/services/transaction_types.ts (75%) create mode 100644 x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts create mode 100644 x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts create mode 100644 x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts create mode 100644 x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts create mode 100644 x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts create mode 100644 x-pack/test/apm_api_integration/tests/settings/custom_link.ts rename x-pack/test/apm_api_integration/{basic => }/tests/traces/__snapshots__/top_traces.snap (99%) rename x-pack/test/apm_api_integration/{basic => }/tests/traces/top_traces.ts (81%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/__snapshots__/breakdown.snap (98%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/__snapshots__/error_rate.snap (95%) create mode 100644 x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/__snapshots__/top_transaction_groups.snap (96%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/__snapshots__/transaction_charts.snap (100%) rename x-pack/test/apm_api_integration/{trial => }/tests/transactions/__snapshots__/transactions_charts.snap (100%) create mode 100644 x-pack/test/apm_api_integration/tests/transactions/breakdown.ts rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/distribution.ts (85%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/error_rate.ts (71%) create mode 100644 x-pack/test/apm_api_integration/tests/transactions/latency.ts rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/throughput.ts (54%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/top_transaction_groups.ts (80%) rename x-pack/test/apm_api_integration/{basic => }/tests/transactions/transactions_groups_overview.ts (94%) delete mode 100644 x-pack/test/apm_api_integration/trial/tests/correlations/slow_transactions.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/index.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/services/top_services.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/settings/custom_link.ts delete mode 100644 x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap delete mode 100644 x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts diff --git a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts index 6efc8774b0139..e78cbbb3aed02 100644 --- a/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts +++ b/packages/kbn-test/src/functional_test_runner/fake_mocha_types.d.ts @@ -20,6 +20,8 @@ export interface Suite { title: string; file?: string; parent?: Suite; + eachTest: (cb: (test: Test) => void) => void; + root: boolean; } export interface Test { diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts index 2a238cdeb5385..a8b0252e6f51c 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.test.ts @@ -6,21 +6,44 @@ * Public License, v 1. */ -import { Test } from '../../fake_mocha_types'; +import { Suite, Test } from '../../fake_mocha_types'; import { Lifecycle } from '../lifecycle'; import { decorateSnapshotUi, expectSnapshot } from './decorate_snapshot_ui'; import path from 'path'; import fs from 'fs'; +const createMockSuite = ({ tests, root = true }: { tests: Test[]; root?: boolean }) => { + const suite = { + tests, + root, + eachTest: (cb: (test: Test) => void) => { + suite.tests.forEach((test) => cb(test)); + }, + } as Suite; + + return suite; +}; + const createMockTest = ({ title = 'Test', passed = true, -}: { title?: string; passed?: boolean } = {}) => { - return { + filename = __filename, + parent, +}: { title?: string; passed?: boolean; filename?: string; parent?: Suite } = {}) => { + const test = ({ + file: filename, fullTitle: () => title, isPassed: () => passed, - parent: {}, - } as Test; + } as unknown) as Test; + + if (parent) { + parent.tests.push(test); + test.parent = parent; + } else { + test.parent = createMockSuite({ tests: [test] }); + } + + return test; }; describe('decorateSnapshotUi', () => { @@ -211,7 +234,7 @@ exports[\`Test2 1\`] = \`"bar"\`; expectSnapshot('bar').toMatch(); }).not.toThrow(); - const afterTestSuite = lifecycle.afterTestSuite.trigger({}); + const afterTestSuite = lifecycle.afterTestSuite.trigger(test.parent); await expect(afterTestSuite).resolves.toBe(undefined); }); @@ -225,7 +248,7 @@ exports[\`Test2 1\`] = \`"bar"\`; expectSnapshot('foo').toMatch(); }).not.toThrow(); - const afterTestSuite = lifecycle.afterTestSuite.trigger({}); + const afterTestSuite = lifecycle.afterTestSuite.trigger(test.parent); await expect(afterTestSuite).rejects.toMatchInlineSnapshot(` [Error: 1 obsolete snapshot(s) found: @@ -234,6 +257,32 @@ exports[\`Test2 1\`] = \`"bar"\`; Run tests again with \`--updateSnapshots\` to remove them.] `); }); + + it('does not throw on unused when some tests are skipped', async () => { + const root = createMockSuite({ tests: [] }); + + const test = createMockTest({ + title: 'Test', + parent: root, + passed: true, + }); + + createMockTest({ + title: 'Test2', + parent: root, + passed: false, + }); + + await lifecycle.beforeEachTest.trigger(test); + + expect(() => { + expectSnapshot('foo').toMatch(); + }).not.toThrow(); + + const afterTestSuite = lifecycle.afterTestSuite.trigger(root); + + await expect(afterTestSuite).resolves.toBeUndefined(); + }); }); }); }); diff --git a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts index 2111f1a6e5e90..4a52299ecf6d1 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/snapshots/decorate_snapshot_ui.ts @@ -16,9 +16,8 @@ import path from 'path'; import prettier from 'prettier'; import babelTraverse from '@babel/traverse'; import { once } from 'lodash'; -import callsites from 'callsites'; import { Lifecycle } from '../lifecycle'; -import { Test } from '../../fake_mocha_types'; +import { Suite, Test } from '../../fake_mocha_types'; type ISnapshotState = InstanceType; @@ -33,12 +32,12 @@ const globalState: { updateSnapshot: SnapshotUpdateState; registered: boolean; currentTest: Test | null; - snapshots: Array<{ tests: Test[]; file: string; snapshotState: ISnapshotState }>; + snapshotStates: Record; } = { updateSnapshot: 'none', registered: false, currentTest: null, - snapshots: [], + snapshotStates: {}, }; const modifyStackTracePrepareOnce = once(() => { @@ -73,7 +72,7 @@ export function decorateSnapshotUi({ isCi: boolean; }) { globalState.registered = true; - globalState.snapshots.length = 0; + globalState.snapshotStates = {}; globalState.currentTest = null; if (isCi) { @@ -102,32 +101,36 @@ export function decorateSnapshotUi({ globalState.currentTest = test; }); - lifecycle.afterTestSuite.add(function (testSuite) { + lifecycle.afterTestSuite.add(function (testSuite: Suite) { // save snapshot & check unused after top-level test suite completes - if (testSuite.parent?.parent) { + if (!testSuite.root) { return; } - const unused: string[] = []; + testSuite.eachTest((test) => { + const file = test.file; - globalState.snapshots.forEach((snapshot) => { - const { tests, snapshotState } = snapshot; - tests.forEach((test) => { - const title = test.fullTitle(); - // If test is failed or skipped, mark snapshots as used. Otherwise, - // running a test in isolation will generate false positives. - if (!test.isPassed()) { - snapshotState.markSnapshotsAsCheckedForTest(title); - } - }); + if (!file) { + return; + } + + const snapshotState = globalState.snapshotStates[file]; + + if (snapshotState && !test.isPassed()) { + snapshotState.markSnapshotsAsCheckedForTest(test.fullTitle()); + } + }); + + const unused: string[] = []; - if (globalState.updateSnapshot !== 'all') { - unused.push(...snapshotState.getUncheckedKeys()); - } else { - snapshotState.removeUncheckedKeys(); + Object.values(globalState.snapshotStates).forEach((state) => { + if (globalState.updateSnapshot === 'all') { + state.removeUncheckedKeys(); } - snapshotState.save(); + unused.push(...state.getUncheckedKeys()); + + state.save(); }); if (unused.length) { @@ -138,7 +141,7 @@ export function decorateSnapshotUi({ ); } - globalState.snapshots.length = 0; + globalState.snapshotStates = {}; }); } @@ -161,43 +164,29 @@ function getSnapshotState(file: string, updateSnapshot: SnapshotUpdateState) { export function expectSnapshot(received: any) { if (!globalState.registered) { - throw new Error( - 'Mocha hooks were not registered before expectSnapshot was used. Call `registerMochaHooksForSnapshots` in your top-level describe().' - ); - } - - if (!globalState.currentTest) { - throw new Error('expectSnapshot can only be called inside of an it()'); + throw new Error('expectSnapshot UI was not initialized before calling expectSnapshot()'); } - const [, fileOfTest] = callsites().map((site) => site.getFileName()); + const test = globalState.currentTest; - if (!fileOfTest) { - throw new Error("Couldn't infer a filename for the current test"); + if (!test) { + throw new Error('expectSnapshot can only be called inside of an it()'); } - let snapshot = globalState.snapshots.find(({ file }) => file === fileOfTest); - - if (!snapshot) { - snapshot = { - file: fileOfTest, - tests: [], - snapshotState: getSnapshotState(fileOfTest, globalState.updateSnapshot), - }; - globalState.snapshots.unshift(snapshot!); + if (!test.file) { + throw new Error('File for test not found'); } - if (!snapshot) { - throw new Error('Snapshot is undefined'); - } + let snapshotState = globalState.snapshotStates[test.file]; - if (!snapshot.tests.includes(globalState.currentTest)) { - snapshot.tests.push(globalState.currentTest); + if (!snapshotState) { + snapshotState = getSnapshotState(test.file, globalState.updateSnapshot); + globalState.snapshotStates[test.file] = snapshotState; } const context: SnapshotContext = { - snapshotState: snapshot.snapshotState, - currentTestName: globalState.currentTest.fullTitle(), + snapshotState, + currentTestName: test.fullTitle(), }; return { diff --git a/x-pack/test/apm_api_integration/basic/config.ts b/x-pack/test/apm_api_integration/basic/config.ts index 03b8b21bf3232..f3e6ec29f3df2 100644 --- a/x-pack/test/apm_api_integration/basic/config.ts +++ b/x-pack/test/apm_api_integration/basic/config.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createTestConfig } from '../common/config'; +import { configs } from '../configs'; -export default createTestConfig({ - license: 'basic', - name: 'X-Pack APM API integration tests (basic)', - testFiles: [require.resolve('./tests')], -}); +export default configs.basic; diff --git a/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts b/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts deleted file mode 100644 index 46c0dbeb8940f..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/alerts/chart_preview.ts +++ /dev/null @@ -1,124 +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 { format } from 'url'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const supertest = getService('supertest'); - const archiveName = 'apm_8.0.0'; - const { end } = archives[archiveName]; - const start = new Date(Date.parse(end) - 600000).toISOString(); - - describe('Alerting chart previews', () => { - describe('GET /api/apm/alerts/chart_preview/transaction_error_rate', () => { - const url = format({ - pathname: '/api/apm/alerts/chart_preview/transaction_error_rate', - query: { - start, - end, - transactionType: 'request', - serviceName: 'opbeans-java', - }, - }); - - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get(url); - - 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 correct data', async () => { - const response = await supertest.get(url); - - expect(response.status).to.be(200); - expect( - response.body.some((item: { x: number; y: number | null }) => item.x && item.y) - ).to.equal(true); - }); - }); - }); - - describe('GET /api/apm/alerts/chart_preview/transaction_error_count', () => { - const url = format({ - pathname: '/api/apm/alerts/chart_preview/transaction_error_count', - query: { - start, - end, - serviceName: 'opbeans-java', - }, - }); - - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get(url); - - 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 correct data', async () => { - const response = await supertest.get(url); - - expect(response.status).to.be(200); - expect( - response.body.some((item: { x: number; y: number | null }) => item.x && item.y) - ).to.equal(true); - }); - }); - }); - - describe('GET /api/apm/alerts/chart_preview/transaction_duration', () => { - const url = format({ - pathname: '/api/apm/alerts/chart_preview/transaction_duration', - query: { - start, - end, - serviceName: 'opbeans-java', - transactionType: 'request', - }, - }); - - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get(url); - - 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 correct data', async () => { - const response = await supertest.get(url); - - expect(response.status).to.be(200); - expect( - response.body.some((item: { x: number; y: number | null }) => item.x && item.y) - ).to.equal(true); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/index.ts b/x-pack/test/apm_api_integration/basic/tests/index.ts deleted file mode 100644 index 4e66c6e6f76c3..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/index.ts +++ /dev/null @@ -1,72 +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 { FtrProviderContext } from '../../common/ftr_provider_context'; - -export default function apmApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('APM specs (basic)', function () { - this.tags('ciGroup1'); - - loadTestFile(require.resolve('./feature_controls')); - - describe('Alerts', function () { - loadTestFile(require.resolve('./alerts/chart_preview')); - }); - - describe('Service Maps', function () { - loadTestFile(require.resolve('./service_maps/service_maps')); - }); - - describe('Services', function () { - loadTestFile(require.resolve('./services/agent_name')); - loadTestFile(require.resolve('./services/annotations')); - loadTestFile(require.resolve('./services/throughput')); - loadTestFile(require.resolve('./services/top_services')); - loadTestFile(require.resolve('./services/transaction_types')); - loadTestFile(require.resolve('./services/service_details')); - loadTestFile(require.resolve('./services/service_icons')); - }); - - describe('Service overview', function () { - loadTestFile(require.resolve('./service_overview/error_groups')); - loadTestFile(require.resolve('./service_overview/dependencies')); - loadTestFile(require.resolve('./service_overview/instances')); - }); - - describe('Settings', function () { - loadTestFile(require.resolve('./settings/custom_link')); - loadTestFile(require.resolve('./settings/agent_configuration')); - - describe('Anomaly detection', function () { - loadTestFile(require.resolve('./settings/anomaly_detection/no_access_user')); - loadTestFile(require.resolve('./settings/anomaly_detection/read_user')); - loadTestFile(require.resolve('./settings/anomaly_detection/write_user')); - }); - }); - - describe('Traces', function () { - loadTestFile(require.resolve('./traces/top_traces')); - }); - - describe('Transactions', function () { - loadTestFile(require.resolve('./transactions/top_transaction_groups')); - loadTestFile(require.resolve('./transactions/latency')); - loadTestFile(require.resolve('./transactions/throughput')); - loadTestFile(require.resolve('./transactions/error_rate')); - loadTestFile(require.resolve('./transactions/breakdown')); - loadTestFile(require.resolve('./transactions/distribution')); - loadTestFile(require.resolve('./transactions/transactions_groups_overview')); - }); - - describe('Observability overview', function () { - loadTestFile(require.resolve('./observability_overview/has_data')); - loadTestFile(require.resolve('./observability_overview/observability_overview')); - }); - - describe('Metrics', function () { - loadTestFile(require.resolve('./metrics_charts/metrics_charts')); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts deleted file mode 100644 index d52aa2727d651..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/metrics_charts/metrics_charts.ts +++ /dev/null @@ -1,439 +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 { first } from 'lodash'; -import { MetricsChartsByAgentAPIResponse } from '../../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; -import { GenericMetricsChart } from '../../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -interface ChartResponse { - body: MetricsChartsByAgentAPIResponse; - status: number; -} - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('when data is loaded', () => { - before(() => esArchiver.load('metrics_8.0.0')); - after(() => esArchiver.unload('metrics_8.0.0')); - - describe('for opbeans-node', () => { - const start = encodeURIComponent('2020-09-08T14:50:00.000Z'); - const end = encodeURIComponent('2020-09-08T14:55:00.000Z'); - const uiFilters = encodeURIComponent(JSON.stringify({})); - const agentName = 'nodejs'; - - describe('returns metrics data', () => { - let chartsResponse: ChartResponse; - before(async () => { - chartsResponse = await supertest.get( - `/api/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` - ); - }); - it('contains CPU usage and System memory usage chart data', async () => { - expect(chartsResponse.status).to.be(200); - expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` - Array [ - "CPU usage", - "System memory usage", - ] - `); - }); - - describe('CPU usage', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find(({ key }) => key === 'cpu_usage_chart'); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "System max", - "System average", - "Process max", - "Process average", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0.714, - 0.3877, - 0.75, - 0.2543, - ] - `); - }); - }); - - describe("System memory usage (using 'system.memory' fields to calculate the memory usage)", () => { - let systemMemoryUsageChart: GenericMetricsChart | undefined; - before(() => { - systemMemoryUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'memory_usage_chart' - ); - }); - - it('has correct series', () => { - expect(systemMemoryUsageChart).to.not.empty(); - expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Max", - "Average", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0.722093920925555, - 0.718173546796348, - ] - `); - }); - }); - }); - }); - - describe('for opbeans-java', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); - const agentName = 'java'; - - describe('returns metrics data', () => { - const start = encodeURIComponent('2020-09-08T14:55:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:00:00.000Z'); - - let chartsResponse: ChartResponse; - before(async () => { - chartsResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` - ); - }); - - it('has correct chart data', async () => { - expect(chartsResponse.status).to.be(200); - expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` - Array [ - "CPU usage", - "System memory usage", - "Heap Memory", - "Non-Heap Memory", - "Thread Count", - "Garbage collection per minute", - "Garbage collection time spent per minute", - ] - `); - }); - - describe('CPU usage', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find(({ key }) => key === 'cpu_usage_chart'); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "System max", - "System average", - "Process max", - "Process average", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0.203, - 0.178777777777778, - 0.01, - 0.009, - ] - `); - }); - - it('has the correct rate', async () => { - const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 0.193, - 0.193, - 0.009, - 0.009, - ] - `); - }); - }); - - describe("System memory usage (using 'system.process.cgroup' fields to calculate the memory usage)", () => { - let systemMemoryUsageChart: GenericMetricsChart | undefined; - before(() => { - systemMemoryUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'memory_usage_chart' - ); - }); - - it('has correct series', () => { - expect(systemMemoryUsageChart).to.not.empty(); - expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Max", - "Average", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0.707924703557837, - 0.705395980841182, - ] - `); - }); - - it('has the correct rate', async () => { - const yValues = systemMemoryUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 0.707924703557837, - 0.707924703557837, - ] - `); - }); - }); - - describe('Heap Memory', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'heap_memory_area_chart' - ); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Avg. used", - "Avg. committed", - "Avg. limit", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 222501617.777778, - 374341632, - 1560281088, - ] - `); - }); - - it('has the correct rate', async () => { - const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 211472896, - 374341632, - 1560281088, - ] - `); - }); - }); - - describe('Non-Heap Memory', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'non_heap_memory_area_chart' - ); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Avg. used", - "Avg. committed", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 138573397.333333, - 147677639.111111, - ] - `); - }); - - it('has the correct rate', async () => { - const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 138162752, - 147386368, - ] - `); - }); - }); - - describe('Thread Count', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'thread_count_line_chart' - ); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Avg. count", - "Max count", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 44.4444444444444, - 45, - ] - `); - }); - - it('has the correct rate', async () => { - const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 44, - 44, - ] - `); - }); - }); - - describe('Garbage collection per minute', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'gc_rate_line_chart' - ); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "G1 Old Generation", - "G1 Young Generation", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0, - 15, - ] - `); - }); - }); - - describe('Garbage collection time spent per minute', () => { - let cpuUsageChart: GenericMetricsChart | undefined; - before(() => { - cpuUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'gc_time_line_chart' - ); - }); - - it('has correct series', () => { - expect(cpuUsageChart).to.not.empty(); - expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "G1 Old Generation", - "G1 Young Generation", - ] - `); - }); - - it('has correct series overall values', () => { - expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0, - 187.5, - ] - `); - }); - }); - }); - - // 9223372036854771712 = memory limit for a c-group when no memory limit is specified - it('calculates system memory usage using system total field when cgroup limit is equal to 9223372036854771712', async () => { - const start = encodeURIComponent('2020-09-08T15:00:30.000Z'); - const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); - - const chartsResponse: ChartResponse = await supertest.get( - `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` - ); - - const systemMemoryUsageChart = chartsResponse.body.charts.find( - ({ key }) => key === 'memory_usage_chart' - ); - - expect(systemMemoryUsageChart).to.not.empty(); - expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)).toMatchInline(` - Array [ - "Max", - "Average", - ] - `); - expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) - .toMatchInline(` - Array [ - 0.114523896426499, - 0.114002376090415, - ] - `); - - const yValues = systemMemoryUsageChart?.series.map((serie) => first(serie.data)?.y); - expectSnapshot(yValues).toMatchInline(` - Array [ - 0.11383724014064, - 0.11383724014064, - ] - `); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts deleted file mode 100644 index f44b1561f2a5a..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/service_maps/service_maps.ts +++ /dev/null @@ -1,32 +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/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function serviceMapsApiTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - const archiveName = 'apm_8.0.0'; - const metadata = archives_metadata[archiveName]; - - // url parameters - const start = encodeURIComponent(metadata.start); - const end = encodeURIComponent(metadata.end); - - describe('Service Maps', () => { - it('is only be available to users with Platinum license (or higher)', async () => { - const response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); - - expect(response.status).to.be(403); - - expectSnapshot(response.body.message).toMatchInline( - `"In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data."` - ); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/services/annotations.ts b/x-pack/test/apm_api_integration/basic/tests/services/annotations.ts deleted file mode 100644 index 3136dcef2e985..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/services/annotations.ts +++ /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 expect from '@kbn/expect'; -import { JsonObject } from 'src/plugins/kibana_utils/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function annotationApiTests({ getService }: FtrProviderContext) { - const supertestWrite = getService('supertestAsApmAnnotationsWriteUser'); - - function request({ method, url, data }: { method: string; url: string; data?: JsonObject }) { - switch (method.toLowerCase()) { - case 'post': - return supertestWrite.post(url).send(data).set('kbn-xsrf', 'foo'); - - default: - throw new Error(`Unsupported method ${method}`); - } - } - - describe('APM annotations with a basic license', () => { - describe('when creating an annotation', () => { - it('fails with a 403 forbidden', async () => { - const response = await request({ - url: '/api/apm/services/opbeans-java/annotation', - method: 'POST', - data: { - '@timestamp': new Date().toISOString(), - message: 'New deployment', - tags: ['foo'], - service: { - version: '1.1', - environment: 'production', - }, - }, - }); - - expect(response.status).to.be(403); - expect(response.body.message).to.be( - 'Annotations require at least a gold license or a trial license.' - ); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/services/throughput.ts b/x-pack/test/apm_api_integration/basic/tests/services/throughput.ts deleted file mode 100644 index 07a1442d751b4..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/services/throughput.ts +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import qs from 'querystring'; -import { first, last } from 'lodash'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -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]; - - describe('Throughput', () => { - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-java/throughput?${qs.stringify({ - start: metadata.start, - end: metadata.end, - uiFilters: encodeURIComponent('{}'), - transactionType: 'request', - })}` - ); - expect(response.status).to.be(200); - expect(response.body.throughput.length).to.be(0); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - describe('returns the service throughput', () => { - let throughputResponse: { - throughput: Array<{ x: number; y: number | null }>; - }; - before(async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-java/throughput?${qs.stringify({ - start: metadata.start, - end: metadata.end, - uiFilters: encodeURIComponent('{}'), - transactionType: 'request', - })}` - ); - throughputResponse = response.body; - }); - - it('returns some data', () => { - expect(throughputResponse.throughput.length).to.be.greaterThan(0); - - const nonNullDataPoints = throughputResponse.throughput.filter(({ y }) => y !== null); - - expect(nonNullDataPoints.length).to.be.greaterThan(0); - }); - - it('has the correct start date', () => { - expectSnapshot( - new Date(first(throughputResponse.throughput)?.x ?? NaN).toISOString() - ).toMatchInline(`"2020-12-08T13:57:30.000Z"`); - }); - - it('has the correct end date', () => { - expectSnapshot( - new Date(last(throughputResponse.throughput)?.x ?? NaN).toISOString() - ).toMatchInline(`"2020-12-08T14:27:30.000Z"`); - }); - - it('has the correct number of buckets', () => { - expectSnapshot(throughputResponse.throughput.length).toMatchInline(`61`); - }); - - it('has the correct throughput', () => { - expectSnapshot(throughputResponse.throughput).toMatch(); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts b/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts deleted file mode 100644 index 98bfe84cf56ee..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/services/top_services.ts +++ /dev/null @@ -1,259 +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 { isEmpty, pick, sortBy } from 'lodash'; -import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - - const range = archives_metadata[archiveName]; - - // url parameters - const start = encodeURIComponent(range.start); - const end = encodeURIComponent(range.end); - - const uiFilters = encodeURIComponent(JSON.stringify({})); - - describe('APM Services Overview', () => { - describe('when data is not loaded ', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - - expect(response.status).to.be(200); - expect(response.body.hasHistoricalData).to.be(false); - expect(response.body.hasLegacyData).to.be(false); - expect(response.body.items.length).to.be(0); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - describe('and fetching a list of services', () => { - let response: { - status: number; - body: APIReturnType<'GET /api/apm/services'>; - }; - - let sortedItems: typeof response.body.items; - - before(async () => { - response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - sortedItems = sortBy(response.body.items, 'serviceName'); - }); - - it('the response is successful', () => { - expect(response.status).to.eql(200); - }); - - it('returns hasHistoricalData: true', () => { - expect(response.body.hasHistoricalData).to.be(true); - }); - - it('returns hasLegacyData: false', () => { - expect(response.body.hasLegacyData).to.be(false); - }); - - it('returns the correct service names', () => { - expectSnapshot(sortedItems.map((item) => item.serviceName)).toMatchInline(` - Array [ - "kibana", - "kibana-frontend", - "opbeans-dotnet", - "opbeans-go", - "opbeans-java", - "opbeans-node", - "opbeans-python", - "opbeans-ruby", - "opbeans-rum", - ] - `); - }); - - it('returns the correct metrics averages', () => { - expectSnapshot( - sortedItems.map((item) => - pick( - item, - 'transactionErrorRate.value', - 'avgResponseTime.value', - 'transactionsPerMinute.value' - ) - ) - ).toMatchInline(` - Array [ - Object { - "avgResponseTime": Object { - "value": 420419.34550767, - }, - "transactionErrorRate": Object { - "value": 0, - }, - "transactionsPerMinute": Object { - "value": 45.6333333333333, - }, - }, - Object { - "avgResponseTime": Object { - "value": 2382833.33333333, - }, - "transactionErrorRate": Object { - "value": null, - }, - "transactionsPerMinute": Object { - "value": 0.2, - }, - }, - Object { - "avgResponseTime": Object { - "value": 631521.83908046, - }, - "transactionErrorRate": Object { - "value": 0.0229885057471264, - }, - "transactionsPerMinute": Object { - "value": 2.9, - }, - }, - Object { - "avgResponseTime": Object { - "value": 27946.1484375, - }, - "transactionErrorRate": Object { - "value": 0.015625, - }, - "transactionsPerMinute": Object { - "value": 4.26666666666667, - }, - }, - Object { - "avgResponseTime": Object { - "value": 237339.813333333, - }, - "transactionErrorRate": Object { - "value": 0.16, - }, - "transactionsPerMinute": Object { - "value": 2.5, - }, - }, - Object { - "avgResponseTime": Object { - "value": 24920.1052631579, - }, - "transactionErrorRate": Object { - "value": 0.0210526315789474, - }, - "transactionsPerMinute": Object { - "value": 3.16666666666667, - }, - }, - Object { - "avgResponseTime": Object { - "value": 29542.6607142857, - }, - "transactionErrorRate": Object { - "value": 0.0357142857142857, - }, - "transactionsPerMinute": Object { - "value": 1.86666666666667, - }, - }, - Object { - "avgResponseTime": Object { - "value": 70518.9328358209, - }, - "transactionErrorRate": Object { - "value": 0.0373134328358209, - }, - "transactionsPerMinute": Object { - "value": 4.46666666666667, - }, - }, - Object { - "avgResponseTime": Object { - "value": 2319812.5, - }, - "transactionErrorRate": Object { - "value": null, - }, - "transactionsPerMinute": Object { - "value": 0.533333333333333, - }, - }, - ] - `); - }); - - it('returns environments', () => { - expectSnapshot(sortedItems.map((item) => item.environments ?? [])).toMatchInline(` - Array [ - Array [ - "production", - ], - Array [ - "production", - ], - Array [ - "production", - ], - Array [ - "testing", - ], - Array [ - "production", - ], - Array [ - "testing", - ], - Array [], - Array [], - Array [ - "testing", - ], - ] - `); - }); - - it(`RUM services don't report any transaction error rates`, () => { - // RUM transactions don't have event.outcome set, - // so they should not have an error rate - - const rumServices = sortedItems.filter((item) => item.agentName === 'rum-js'); - - expect(rumServices.length).to.be.greaterThan(0); - - expect(rumServices.every((item) => isEmpty(item.transactionErrorRate?.value))); - }); - - it('non-RUM services all report transaction error rates', () => { - const nonRumServices = sortedItems.filter((item) => item.agentName !== 'rum-js'); - - expect( - nonRumServices.every((item) => { - return ( - typeof item.transactionErrorRate?.value === 'number' && - item.transactionErrorRate.timeseries.length > 0 - ); - }) - ).to.be(true); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts b/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts deleted file mode 100644 index 1817c7c4511fa..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/settings/agent_configuration.ts +++ /dev/null @@ -1,489 +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 { omit, orderBy } from 'lodash'; -import { AgentConfigurationIntake } from '../../../../../plugins/apm/common/agent_configuration/configuration_types'; -import { AgentConfigSearchParams } from '../../../../../plugins/apm/server/routes/settings/agent_configuration'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function agentConfigurationTests({ getService }: FtrProviderContext) { - const supertestRead = getService('supertestAsApmReadUser'); - const supertestWrite = getService('supertestAsApmWriteUser'); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - - function getServices() { - return supertestRead - .get(`/api/apm/settings/agent-configuration/services`) - .set('kbn-xsrf', 'foo'); - } - - function getEnvironments(serviceName: string) { - return supertestRead - .get(`/api/apm/settings/agent-configuration/environments?serviceName=${serviceName}`) - .set('kbn-xsrf', 'foo'); - } - - function getAgentName(serviceName: string) { - return supertestRead - .get(`/api/apm/settings/agent-configuration/agent_name?serviceName=${serviceName}`) - .set('kbn-xsrf', 'foo'); - } - - function searchConfigurations(configuration: AgentConfigSearchParams) { - return supertestRead - .post(`/api/apm/settings/agent-configuration/search`) - .send(configuration) - .set('kbn-xsrf', 'foo'); - } - - function getAllConfigurations() { - return supertestRead.get(`/api/apm/settings/agent-configuration`).set('kbn-xsrf', 'foo'); - } - - async function createConfiguration(config: AgentConfigurationIntake, { user = 'write' } = {}) { - log.debug('creating configuration', config.service); - const supertestClient = user === 'read' ? supertestRead : supertestWrite; - - const res = await supertestClient - .put(`/api/apm/settings/agent-configuration`) - .send(config) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - async function updateConfiguration(config: AgentConfigurationIntake, { user = 'write' } = {}) { - log.debug('updating configuration', config.service); - const supertestClient = user === 'read' ? supertestRead : supertestWrite; - - const res = await supertestClient - .put(`/api/apm/settings/agent-configuration?overwrite=true`) - .send(config) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - async function deleteConfiguration( - { service }: AgentConfigurationIntake, - { user = 'write' } = {} - ) { - log.debug('deleting configuration', service); - const supertestClient = user === 'read' ? supertestRead : supertestWrite; - - const res = await supertestClient - .delete(`/api/apm/settings/agent-configuration`) - .send({ service }) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - function throwOnError(res: any) { - const { statusCode, req, body } = res; - if (statusCode !== 200) { - const e = new Error(` - Endpoint: ${req.method} ${req.path} - Service: ${JSON.stringify(res.request._data.service)} - Status code: ${statusCode} - Response: ${body.message}`); - - // @ts-ignore - e.res = res; - - throw e; - } - } - - describe('agent configuration', () => { - describe('when no data is loaded', () => { - it('handles the empty state for services', async () => { - const { body } = await getServices(); - expect(body).to.eql(['ALL_OPTION_VALUE']); - }); - - it('handles the empty state for environments', async () => { - const { body } = await getEnvironments('myservice'); - expect(body).to.eql([{ name: 'ALL_OPTION_VALUE', alreadyConfigured: false }]); - }); - - it('handles the empty state for agent names', async () => { - const { body } = await getAgentName('myservice'); - expect(body).to.eql({}); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - it('returns all services', async () => { - const { body } = await getServices(); - expectSnapshot(body).toMatchInline(` - Array [ - "ALL_OPTION_VALUE", - "kibana", - "kibana-frontend", - "opbeans-dotnet", - "opbeans-go", - "opbeans-java", - "opbeans-node", - "opbeans-python", - "opbeans-ruby", - "opbeans-rum", - ] - `); - }); - - it('returns the environments, all unconfigured', async () => { - const { body } = await getEnvironments('opbeans-node'); - - expect(body.map((item: { name: string }) => item.name)).to.contain('ALL_OPTION_VALUE'); - - expect( - body.every((item: { alreadyConfigured: boolean }) => item.alreadyConfigured === false) - ).to.be(true); - - expectSnapshot(body).toMatchInline(` - Array [ - Object { - "alreadyConfigured": false, - "name": "ALL_OPTION_VALUE", - }, - Object { - "alreadyConfigured": false, - "name": "testing", - }, - ] - `); - }); - - it('returns the agent names', async () => { - const { body } = await getAgentName('opbeans-node'); - expect(body).to.eql({ agentName: 'nodejs' }); - }); - }); - - describe('as a read-only user', () => { - const newConfig = { service: {}, settings: { transaction_sample_rate: '0.55' } }; - it('throws when attempting to create config', async () => { - try { - await createConfiguration(newConfig, { user: 'read' }); - - // ensure that `createConfiguration` throws - expect(true).to.be(false); - } catch (e) { - expect(e.res.statusCode).to.be(403); - } - }); - - describe('when a configuration already exists', () => { - before(async () => createConfiguration(newConfig)); - after(async () => deleteConfiguration(newConfig)); - - it('throws when attempting to update config', async () => { - try { - await updateConfiguration(newConfig, { user: 'read' }); - - // ensure that `updateConfiguration` throws - expect(true).to.be(false); - } catch (e) { - expect(e.res.statusCode).to.be(403); - } - }); - - it('throws when attempting to delete config', async () => { - try { - await deleteConfiguration(newConfig, { user: 'read' }); - - // ensure that `deleteConfiguration` throws - expect(true).to.be(false); - } catch (e) { - expect(e.res.statusCode).to.be(403); - } - }); - }); - }); - - describe('when creating one configuration', () => { - const newConfig = { - service: {}, - settings: { transaction_sample_rate: '0.55' }, - }; - - const searchParams = { - service: { name: 'myservice', environment: 'development' }, - etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39', - }; - - it('can create and delete config', async () => { - // assert that config does not exist - const res1 = await searchConfigurations(searchParams); - expect(res1.status).to.equal(404); - - // assert that config was created - await createConfiguration(newConfig); - const res2 = await searchConfigurations(searchParams); - expect(res2.status).to.equal(200); - - // assert that config was deleted - await deleteConfiguration(newConfig); - const res3 = await searchConfigurations(searchParams); - expect(res3.status).to.equal(404); - }); - - describe('when a configuration exists', () => { - before(async () => createConfiguration(newConfig)); - after(async () => deleteConfiguration(newConfig)); - - it('can find the config', async () => { - const { status, body } = await searchConfigurations(searchParams); - expect(status).to.equal(200); - expect(body._source.service).to.eql({}); - expect(body._source.settings).to.eql({ transaction_sample_rate: '0.55' }); - }); - - it('can list the config', async () => { - const { status, body } = await getAllConfigurations(); - expect(status).to.equal(200); - expect(omitTimestamp(body)).to.eql([ - { - service: {}, - settings: { transaction_sample_rate: '0.55' }, - applied_by_agent: false, - etag: 'eb88a8997666cc4b33745ef355a1bbd7c4782f2d', - }, - ]); - }); - - it('can update the config', async () => { - await updateConfiguration({ service: {}, settings: { transaction_sample_rate: '0.85' } }); - const { status, body } = await searchConfigurations(searchParams); - expect(status).to.equal(200); - expect(body._source.service).to.eql({}); - expect(body._source.settings).to.eql({ transaction_sample_rate: '0.85' }); - }); - }); - }); - - describe('when creating multiple configurations', () => { - const configs = [ - { - service: {}, - settings: { transaction_sample_rate: '0.1' }, - }, - { - service: { name: 'my_service' }, - settings: { transaction_sample_rate: '0.2' }, - }, - { - service: { name: 'my_service', environment: 'development' }, - settings: { transaction_sample_rate: '0.3' }, - }, - { - service: { environment: 'production' }, - settings: { transaction_sample_rate: '0.4' }, - }, - { - service: { environment: 'development' }, - settings: { transaction_sample_rate: '0.5' }, - }, - ]; - - before(async () => { - await Promise.all(configs.map((config) => createConfiguration(config))); - }); - - after(async () => { - await Promise.all(configs.map((config) => deleteConfiguration(config))); - }); - - const agentsRequests = [ - { - service: { name: 'non_existing_service', environment: 'non_existing_env' }, - expectedSettings: { transaction_sample_rate: '0.1' }, - }, - { - service: { name: 'my_service', environment: 'non_existing_env' }, - expectedSettings: { transaction_sample_rate: '0.2' }, - }, - { - service: { name: 'my_service', environment: 'production' }, - expectedSettings: { transaction_sample_rate: '0.2' }, - }, - { - service: { name: 'my_service', environment: 'development' }, - expectedSettings: { transaction_sample_rate: '0.3' }, - }, - { - service: { name: 'non_existing_service', environment: 'production' }, - expectedSettings: { transaction_sample_rate: '0.4' }, - }, - { - service: { name: 'non_existing_service', environment: 'development' }, - expectedSettings: { transaction_sample_rate: '0.5' }, - }, - ]; - - it('can list all configs', async () => { - const { status, body } = await getAllConfigurations(); - expect(status).to.equal(200); - expect(orderBy(omitTimestamp(body), ['settings.transaction_sample_rate'])).to.eql([ - { - service: {}, - settings: { transaction_sample_rate: '0.1' }, - applied_by_agent: false, - etag: '0758cb18817de60cca29e07480d472694239c4c3', - }, - { - service: { name: 'my_service' }, - settings: { transaction_sample_rate: '0.2' }, - applied_by_agent: false, - etag: 'e04737637056fdf1763bf0ef0d3fcb86e89ae5fc', - }, - { - service: { name: 'my_service', environment: 'development' }, - settings: { transaction_sample_rate: '0.3' }, - applied_by_agent: false, - etag: 'af4dac62621b6762e6281481d1f7523af1124120', - }, - { - service: { environment: 'production' }, - settings: { transaction_sample_rate: '0.4' }, - applied_by_agent: false, - etag: '8d1bf8e6b778b60af351117e2cf53fb1ee570068', - }, - { - service: { environment: 'development' }, - settings: { transaction_sample_rate: '0.5' }, - applied_by_agent: false, - etag: '4ce40da57e3c71daca704121c784b911ec05ae81', - }, - ]); - }); - - for (const agentRequest of agentsRequests) { - it(`${agentRequest.service.name} / ${agentRequest.service.environment}`, async () => { - const { status, body } = await searchConfigurations({ - service: agentRequest.service, - etag: 'abc', - }); - - expect(status).to.equal(200); - expect(body._source.settings).to.eql(agentRequest.expectedSettings); - }); - } - }); - - describe('when an agent retrieves a configuration', () => { - const config = { - service: { name: 'myservice', environment: 'development' }, - settings: { transaction_sample_rate: '0.9' }, - }; - const configProduction = { - service: { name: 'myservice', environment: 'production' }, - settings: { transaction_sample_rate: '0.9' }, - }; - let etag: string; - - before(async () => { - log.debug('creating agent configuration'); - await createConfiguration(config); - await createConfiguration(configProduction); - }); - - after(async () => { - await deleteConfiguration(config); - await deleteConfiguration(configProduction); - }); - - it(`should have 'applied_by_agent=false' before supplying etag`, async () => { - const res1 = await searchConfigurations({ - service: { name: 'myservice', environment: 'development' }, - }); - - etag = res1.body._source.etag; - - const res2 = await searchConfigurations({ - service: { name: 'myservice', environment: 'development' }, - etag, - }); - - expect(res1.body._source.applied_by_agent).to.be(false); - expect(res2.body._source.applied_by_agent).to.be(false); - }); - - it(`should have 'applied_by_agent=true' after supplying etag`, async () => { - await searchConfigurations({ - service: { name: 'myservice', environment: 'development' }, - etag, - }); - - async function hasBeenAppliedByAgent() { - const { body } = await searchConfigurations({ - service: { name: 'myservice', environment: 'development' }, - }); - - return body._source.applied_by_agent; - } - - // wait until `applied_by_agent` has been updated in elasticsearch - expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); - }); - it(`should have 'applied_by_agent=false' before marking as applied`, async () => { - const res1 = await searchConfigurations({ - service: { name: 'myservice', environment: 'production' }, - }); - - expect(res1.body._source.applied_by_agent).to.be(false); - }); - it(`should have 'applied_by_agent=true' when 'mark_as_applied_by_agent' attribute is true`, async () => { - await searchConfigurations({ - service: { name: 'myservice', environment: 'production' }, - mark_as_applied_by_agent: true, - }); - - async function hasBeenAppliedByAgent() { - const { body } = await searchConfigurations({ - service: { name: 'myservice', environment: 'production' }, - }); - - return body._source.applied_by_agent; - } - - // wait until `applied_by_agent` has been updated in elasticsearch - expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); - }); - }); - }); -} - -async function waitFor(cb: () => Promise, retries = 50): Promise { - if (retries === 0) { - throw new Error(`Maximum number of retries reached`); - } - - const res = await cb(); - if (!res) { - await new Promise((resolve) => setTimeout(resolve, 100)); - return waitFor(cb, retries - 1); - } - return res; -} - -function omitTimestamp(configs: AgentConfigurationIntake[]) { - return configs.map((config: AgentConfigurationIntake) => omit(config, '@timestamp')); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.ts deleted file mode 100644 index 5630bd195b6cd..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/no_access_user.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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const noAccessUser = getService('supertestAsNoAccessUser'); - - function getAnomalyDetectionJobs() { - return noAccessUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createAnomalyDetectionJobs(environments: string[]) { - return noAccessUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - describe('when user does not have read access to ML', () => { - describe('when calling the endpoint for listing jobs', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await getAnomalyDetectionJobs(); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - - describe('when calling create endpoint', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts deleted file mode 100644 index 30e097e791eaa..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/read_user.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const apmReadUser = getService('supertestAsApmReadUser'); - - function getAnomalyDetectionJobs() { - return apmReadUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createAnomalyDetectionJobs(environments: string[]) { - return apmReadUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - describe('when user has read access to ML', () => { - describe('when calling the endpoint for listing jobs', () => { - it('returns an error because the user is on basic license', async () => { - const { body } = await getAnomalyDetectionJobs(); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - - expectSnapshot(body.message).toMatchInline( - `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` - ); - }); - }); - - describe('when calling create endpoint', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts deleted file mode 100644 index 15659229a1917..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/settings/anomaly_detection/write_user.ts +++ /dev/null @@ -1,50 +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 { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const apmWriteUser = getService('supertestAsApmWriteUser'); - - function getAnomalyDetectionJobs() { - return apmWriteUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createAnomalyDetectionJobs(environments: string[]) { - return apmWriteUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - describe('when user has write access to ML', () => { - describe('when calling the endpoint for listing jobs', () => { - it('returns an error because the user is on basic license', async () => { - const { body } = await getAnomalyDetectionJobs(); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - expectSnapshot(body.message).toMatchInline( - `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` - ); - }); - }); - - describe('when calling create endpoint', () => { - it('returns an error because the user is on basic license', async () => { - const { body } = await createAnomalyDetectionJobs(['production', 'staging']); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - - expectSnapshot(body.message).toMatchInline( - `"To use anomaly detection, you must be subscribed to an Elastic Platinum license. With it, you'll be able to monitor your services with the aid of machine learning."` - ); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts deleted file mode 100644 index 8ac5566fc2c49..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/settings/custom_link.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import expect from '@kbn/expect'; -import { CustomLink } from '../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function customLinksTests({ getService }: FtrProviderContext) { - const supertestWrite = getService('supertestAsApmWriteUser'); - - describe('custom links', () => { - it('is only be available to users with Gold license (or higher)', async () => { - const customLink = { - url: 'https://elastic.co', - label: 'with filters', - filters: [ - { key: 'service.name', value: 'baz' }, - { key: 'transaction.type', value: 'qux' }, - ], - } as CustomLink; - const response = await supertestWrite - .post(`/api/apm/settings/custom_links`) - .send(customLink) - .set('kbn-xsrf', 'foo'); - - expect(response.status).to.be(403); - - expectSnapshot(response.body.message).toMatchInline( - `"To create custom links, you must be subscribed to an Elastic Gold license or above. With it, you'll have the ability to create custom links to improve your workflow when analyzing your services."` - ); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/breakdown.ts b/x-pack/test/apm_api_integration/basic/tests/transactions/breakdown.ts deleted file mode 100644 index 947defca05d94..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/breakdown.ts +++ /dev/null @@ -1,123 +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/fixtures/es_archiver/archives_metadata'; -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 transactionType = 'request'; - const transactionName = 'GET /api'; - const uiFilters = encodeURIComponent(JSON.stringify({})); - - describe('Breakdown', () => { - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` - ); - expect(response.status).to.be(200); - expect(response.body).to.eql({ timeseries: [] }); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - it('returns the transaction breakdown for a service', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatch(); - }); - it('returns the transaction breakdown for a transaction group', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}&transactionName=${transactionName}` - ); - - expect(response.status).to.be(200); - - const { timeseries } = response.body; - - const numberOfSeries = timeseries.length; - - expectSnapshot(numberOfSeries).toMatchInline(`1`); - - const { title, color, type, data, hideLegend, legendValue } = timeseries[0]; - - const nonNullDataPoints = data.filter((y: number | null) => y !== null); - - expectSnapshot(nonNullDataPoints.length).toMatchInline(`61`); - - expectSnapshot( - data.slice(0, 5).map(({ x, y }: { x: number; y: number | null }) => { - return { - x: new Date(x ?? NaN).toISOString(), - y, - }; - }) - ).toMatchInline(` - Array [ - Object { - "x": "2020-12-08T13:57:30.000Z", - "y": null, - }, - Object { - "x": "2020-12-08T13:58:00.000Z", - "y": null, - }, - Object { - "x": "2020-12-08T13:58:30.000Z", - "y": 1, - }, - Object { - "x": "2020-12-08T13:59:00.000Z", - "y": 1, - }, - Object { - "x": "2020-12-08T13:59:30.000Z", - "y": null, - }, - ] - `); - - expectSnapshot(title).toMatchInline(`"app"`); - expectSnapshot(color).toMatchInline(`"#54b399"`); - expectSnapshot(type).toMatchInline(`"areaStacked"`); - expectSnapshot(hideLegend).toMatchInline(`false`); - expectSnapshot(legendValue).toMatchInline(`"100%"`); - - expectSnapshot(data).toMatch(); - }); - it('returns the transaction breakdown sorted by name', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body.timeseries.map((serie: { title: string }) => serie.title)) - .toMatchInline(` - Array [ - "app", - "http", - "postgresql", - "redis", - ] - `); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts deleted file mode 100644 index 3088f4fd481d7..0000000000000 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/latency.ts +++ /dev/null @@ -1,102 +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/fixtures/es_archiver/archives_metadata'; -import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -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]; - - // url parameters - const start = encodeURIComponent(metadata.start); - const end = encodeURIComponent(metadata.end); - const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' })); - - describe('Latency', () => { - describe('when data is not loaded ', () => { - it('returns 400 when latencyAggregationType is not informed', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request` - ); - - expect(response.status).to.be(400); - }); - - it('returns 400 when transactionType is not informed', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - - expect(response.status).to.be(400); - }); - - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request` - ); - - expect(response.status).to.be(200); - - expect(response.body.overallAvgDuration).to.be(null); - expect(response.body.latencyTimeseries.length).to.be(0); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - let response: PromiseReturnType; - - describe('average latency type', () => { - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg` - ); - }); - - it('returns average duration and timeseries', async () => { - expect(response.status).to.be(200); - expect(response.body.overallAvgDuration).not.to.be(null); - expect(response.body.latencyTimeseries.length).to.be.eql(61); - }); - }); - - describe('95th percentile latency type', () => { - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95` - ); - }); - - it('returns average duration and timeseries', async () => { - expect(response.status).to.be(200); - expect(response.body.overallAvgDuration).not.to.be(null); - expect(response.body.latencyTimeseries.length).to.be.eql(61); - }); - }); - - describe('99th percentile latency type', () => { - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99` - ); - }); - - it('returns average duration and timeseries', async () => { - expect(response.status).to.be(200); - expect(response.body.overallAvgDuration).not.to.be(null); - expect(response.body.latencyTimeseries.length).to.be.eql(61); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/common/config.ts b/x-pack/test/apm_api_integration/common/config.ts index f94bd6bd3be6f..08333de15ec6d 100644 --- a/x-pack/test/apm_api_integration/common/config.ts +++ b/x-pack/test/apm_api_integration/common/config.ts @@ -11,11 +11,12 @@ import path from 'path'; import { InheritedFtrProviderContext, InheritedServices } from './ftr_provider_context'; import { PromiseReturnType } from '../../../plugins/observability/typings/common'; import { createApmUser, APM_TEST_PASSWORD, ApmUser } from './authentication'; +import { APMFtrConfigName } from '../configs'; +import { registry } from './registry'; -interface Settings { +interface Config { + name: APMFtrConfigName; license: 'basic' | 'trial'; - testFiles: string[]; - name: string; } const supertestAsApmUser = (kibanaServer: UrlObject, apmUser: ApmUser) => async ( @@ -34,8 +35,8 @@ const supertestAsApmUser = (kibanaServer: UrlObject, apmUser: ApmUser) => async return supertestAsPromised(url); }; -export function createTestConfig(settings: Settings) { - const { testFiles, license, name } = settings; +export function createTestConfig(config: Config) { + const { license, name } = config; return async ({ readConfigFile }: FtrConfigProviderContext) => { const xPackAPITestsConfig = await readConfigFile( @@ -47,8 +48,10 @@ export function createTestConfig(settings: Settings) { const supertestAsApmReadUser = supertestAsApmUser(servers.kibana, ApmUser.apmReadUser); + registry.init(config.name); + return { - testFiles, + testFiles: [require.resolve('../tests')], servers, esArchiver: { directory: path.resolve(__dirname, './fixtures/es_archiver'), @@ -69,7 +72,7 @@ export function createTestConfig(settings: Settings) { ), }, junit: { - reportName: name, + reportName: `APM API Integration tests (${name})`, }, esTestCluster: { ...xPackAPITestsConfig.get('esTestCluster'), diff --git a/x-pack/test/apm_api_integration/common/registry.ts b/x-pack/test/apm_api_integration/common/registry.ts new file mode 100644 index 0000000000000..8c918eae5a5a8 --- /dev/null +++ b/x-pack/test/apm_api_integration/common/registry.ts @@ -0,0 +1,160 @@ +/* + * 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 { castArray, groupBy } from 'lodash'; +import callsites from 'callsites'; +import { maybe } from '../../../plugins/apm/common/utils/maybe'; +import { joinByKey } from '../../../plugins/apm/common/utils/join_by_key'; +import { APMFtrConfigName } from '../configs'; +import { FtrProviderContext } from './ftr_provider_context'; + +type ArchiveName = + | 'apm_8.0.0' + | '8.0.0' + | 'metrics_8.0.0' + | 'ml_8.0.0' + | 'observability_overview' + | 'rum_8.0.0' + | 'rum_test_data'; + +interface RunCondition { + config: APMFtrConfigName; + archives: ArchiveName[]; +} + +const callbacks: Array< + RunCondition & { + runs: Array<{ + cb: () => void; + }>; + } +> = []; + +let configName: APMFtrConfigName | undefined; + +let running: boolean = false; + +export const registry = { + init: (config: APMFtrConfigName) => { + configName = config; + callbacks.length = 0; + running = false; + }, + when: ( + title: string, + conditions: RunCondition | RunCondition[], + callback: (condition: RunCondition) => void + ) => { + const allConditions = castArray(conditions); + + if (!allConditions.length) { + throw new Error('At least one condition should be defined'); + } + + if (running) { + throw new Error("Can't add tests when running"); + } + + const frame = maybe(callsites()[1]); + + const file = frame?.getFileName(); + + if (!file) { + throw new Error('Could not infer file for suite'); + } + + allConditions.forEach((matchedCondition) => { + callbacks.push({ + ...matchedCondition, + runs: [ + { + cb: () => { + const suite = describe(title, () => { + callback(matchedCondition); + }); + + suite.file = file; + suite.eachTest((test) => { + test.file = file; + }); + }, + }, + ], + }); + }); + }, + run: (context: FtrProviderContext) => { + if (!configName) { + throw new Error(`registry was not init() before running`); + } + running = true; + const esArchiver = context.getService('esArchiver'); + const logger = context.getService('log'); + const logWithTimer = () => { + const start = process.hrtime(); + + return (message: string) => { + const diff = process.hrtime(start); + const time = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; + logger.info(`(${time}) ${message}`); + }; + }; + + const groups = joinByKey(callbacks, ['config', 'archives'], (a, b) => ({ + ...a, + ...b, + runs: a.runs.concat(b.runs), + })); + + callbacks.length = 0; + + const byConfig = groupBy(groups, 'config'); + + Object.keys(byConfig).forEach((config) => { + const groupsForConfig = byConfig[config]; + // register suites for other configs, but skip them so tests are marked as such + // and their snapshots are not marked as obsolete + (config === configName ? describe : describe.skip)(config, () => { + groupsForConfig.forEach((group) => { + const { runs, ...condition } = group; + + const runBefore = async () => { + const log = logWithTimer(); + for (const archiveName of condition.archives) { + log(`Loading ${archiveName}`); + await esArchiver.load(archiveName); + } + if (condition.archives.length) { + log('Loaded all archives'); + } + }; + + const runAfter = async () => { + const log = logWithTimer(); + for (const archiveName of condition.archives) { + log(`Unloading ${archiveName}`); + await esArchiver.unload(archiveName); + } + if (condition.archives.length) { + log('Unloaded all archives'); + } + }; + + describe(condition.archives.join(',') || 'no data', () => { + before(runBefore); + + runs.forEach((run) => { + run.cb(); + }); + + after(runAfter); + }); + }); + }); + }); + + running = false; + }, +}; diff --git a/x-pack/test/apm_api_integration/configs/index.ts b/x-pack/test/apm_api_integration/configs/index.ts new file mode 100644 index 0000000000000..4bda5419c8729 --- /dev/null +++ b/x-pack/test/apm_api_integration/configs/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { mapValues } from 'lodash'; +import { createTestConfig } from '../common/config'; + +const apmFtrConfigs = { + basic: { + license: 'basic' as const, + }, + trial: { + license: 'trial' as const, + }, +}; + +export type APMFtrConfigName = keyof typeof apmFtrConfigs; + +export const configs = mapValues(apmFtrConfigs, (value, key) => { + return createTestConfig({ + name: key as APMFtrConfigName, + ...value, + }); +}); diff --git a/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts new file mode 100644 index 0000000000000..2b14e3b8a4a75 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/alerts/chart_preview.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { format } from 'url'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const archiveName = 'apm_8.0.0'; + const { end } = archives[archiveName]; + const start = new Date(Date.parse(end) - 600000).toISOString(); + + const apis = [ + { + pathname: '/api/apm/alerts/chart_preview/transaction_error_rate', + params: { transactionType: 'request' }, + }, + { pathname: '/api/apm/alerts/chart_preview/transaction_error_count', params: {} }, + { + pathname: '/api/apm/alerts/chart_preview/transaction_duration', + params: { transactionType: 'request' }, + }, + ]; + + apis.forEach((api) => { + const url = format({ + pathname: api.pathname, + query: { + start, + end, + serviceName: 'opbeans-java', + ...api.params, + }, + }); + + registry.when( + `GET ${api.pathname} without data loaded`, + { config: 'basic', archives: [] }, + () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + } + ); + + registry.when( + `GET ${api.pathname} with data loaded`, + { config: 'basic', archives: [archiveName] }, + () => { + it('returns the correct data', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect( + response.body.some((item: { x: number; y: number | null }) => item.x && item.y) + ).to.equal(true); + }); + } + ); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/correlations/slow_transactions.ts b/x-pack/test/apm_api_integration/tests/correlations/slow_transactions.ts new file mode 100644 index 0000000000000..2439943a664ea --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/correlations/slow_transactions.ts @@ -0,0 +1,88 @@ +/* + * 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 { format } from 'url'; +import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const archiveName = 'apm_8.0.0'; + const range = archives_metadata[archiveName]; + + const url = format({ + pathname: `/api/apm/correlations/slow_transactions`, + query: { + start: range.start, + end: range.end, + durationPercentile: 95, + fieldNames: + 'user.username,user.id,host.ip,user_agent.name,kubernetes.pod.uuid,url.domain,container.id,service.node.name', + }, + }); + + registry.when('without data', { config: 'trial', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + + expect(response.status).to.be(200); + expect(response.body.response).to.be(undefined); + }); + }); + + registry.when('with data and default args', { config: 'trial', archives: ['apm_8.0.0'] }, () => { + type ResponseBody = APIReturnType<'GET /api/apm/correlations/slow_transactions'>; + let response: { + status: number; + body: NonNullable; + }; + + before(async () => { + response = await supertest.get(url); + }); + + it('returns successfully', () => { + expect(response.status).to.eql(200); + }); + + it('returns significant terms', () => { + const sorted = response.body?.significantTerms?.sort(); + expectSnapshot(sorted?.map((term) => term.fieldName)).toMatchInline(` + Array [ + "user_agent.name", + "url.domain", + "host.ip", + "service.node.name", + "container.id", + "url.domain", + "user_agent.name", + ] + `); + }); + + it('returns a distribution per term', () => { + expectSnapshot(response.body?.significantTerms?.map((term) => term.distribution.length)) + .toMatchInline(` + Array [ + 11, + 11, + 11, + 11, + 11, + 11, + 11, + ] + `); + }); + + it('returns overall distribution', () => { + expectSnapshot(response.body?.overall?.distribution.length).toMatchInline(`11`); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap b/x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_load_dist.snap similarity index 95% rename from x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap rename to x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_load_dist.snap index c8681866169a5..e21069ba2f0a6 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_load_dist.snap +++ b/x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_load_dist.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (trial) CSM UX page load dist when there is data returns page load distribution 1`] = ` +exports[`APM API tests trial 8.0.0,rum_8.0.0 UX page load dist with data returns page load distribution 1`] = ` Object { "maxDuration": 54.46, "minDuration": 0, @@ -456,7 +456,7 @@ Object { } `; -exports[`APM specs (trial) CSM UX page load dist when there is data returns page load distribution with breakdown 1`] = ` +exports[`APM API tests trial 8.0.0,rum_8.0.0 UX page load dist with data returns page load distribution with breakdown 1`] = ` Array [ Object { "data": Array [ @@ -819,6 +819,6 @@ Array [ ] `; -exports[`APM specs (trial) CSM UX page load dist when there is no data returns empty list 1`] = `Object {}`; +exports[`APM API tests trial no data UX page load dist without data returns empty list 1`] = `Object {}`; -exports[`APM specs (trial) CSM UX page load dist when there is no data returns empty list with breakdowns 1`] = `Object {}`; +exports[`APM API tests trial no data UX page load dist without data returns empty list with breakdowns 1`] = `Object {}`; diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap b/x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_views.snap similarity index 90% rename from x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap rename to x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_views.snap index 76e5180ba2141..01d0d7fb6139c 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/__snapshots__/page_views.snap +++ b/x-pack/test/apm_api_integration/tests/csm/__snapshots__/page_views.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (trial) CSM CSM page views when there is data returns page views 1`] = ` +exports[`APM API tests trial 8.0.0,rum_8.0.0 CSM page views with data returns page views 1`] = ` Object { "items": Array [ Object { @@ -128,7 +128,7 @@ Object { } `; -exports[`APM specs (trial) CSM CSM page views when there is data returns page views with breakdown 1`] = ` +exports[`APM API tests trial 8.0.0,rum_8.0.0 CSM page views with data returns page views with breakdown 1`] = ` Object { "items": Array [ Object { @@ -265,14 +265,14 @@ Object { } `; -exports[`APM specs (trial) CSM CSM page views when there is no data returns empty list 1`] = ` +exports[`APM API tests trial no data CSM page views without data returns empty list 1`] = ` Object { "items": Array [], "topItems": Array [], } `; -exports[`APM specs (trial) CSM CSM page views when there is no data returns empty list with breakdowns 1`] = ` +exports[`APM API tests trial no data CSM page views without data returns empty list with breakdowns 1`] = ` Object { "items": Array [], "topItems": Array [], diff --git a/x-pack/test/apm_api_integration/tests/csm/csm_services.ts b/x-pack/test/apm_api_integration/tests/csm/csm_services.ts new file mode 100644 index 0000000000000..6460c0832a947 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/csm/csm_services.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + registry.when('CSM Services without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/services?start=2020-06-28T10%3A24%3A46.055Z&end=2020-07-29T10%3A24%3A46.055Z&uiFilters=%7B%22agentName%22%3A%5B%22js-base%22%2C%22rum-js%22%5D%7D' + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + + registry.when( + 'CSM services with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { + it('returns rum services list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/services?start=2020-06-28T10%3A24%3A46.055Z&end=2020-07-29T10%3A24%3A46.055Z&uiFilters=%7B%22agentName%22%3A%5B%22js-base%22%2C%22rum-js%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatchInline(`Array []`); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts similarity index 53% rename from x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts rename to x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts index f2033e03f5821..66f99dd29df5a 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/has_rum_data.ts +++ b/x-pack/test/apm_api_integration/tests/csm/has_rum_data.ts @@ -5,38 +5,31 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function rumHasDataApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('CSM has rum data api', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/observability_overview/has_rum_data?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=' - ); + registry.when('has_rum_data without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/observability_overview/has_rum_data?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=' + ); - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatchInline(` + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatchInline(` Object { "hasData": false, } `); - }); }); + }); - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - + registry.when( + 'has RUM data with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { it('returns that it has data and service name with most traffice', async () => { const response = await supertest.get( '/api/apm/observability_overview/has_rum_data?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=' @@ -51,6 +44,6 @@ export default function rumHasDataApiTests({ getService }: FtrProviderContext) { } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts b/x-pack/test/apm_api_integration/tests/csm/js_errors.ts similarity index 72% rename from x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts rename to x-pack/test/apm_api_integration/tests/csm/js_errors.ts index 6abb701f98a1c..dcadc8424ef7e 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/js_errors.ts +++ b/x-pack/test/apm_api_integration/tests/csm/js_errors.ts @@ -5,40 +5,33 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function rumJsErrorsApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('CSM js errors', () => { - describe('when there is no data', () => { - it('returns no js errors', async () => { - const response = await supertest.get( - '/api/apm/rum-client/js-errors?pageSize=5&pageIndex=0&start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' - ); + registry.when('CSM JS errors with data', { config: 'trial', archives: [] }, () => { + it('returns no js errors', async () => { + const response = await supertest.get( + '/api/apm/rum-client/js-errors?pageSize=5&pageIndex=0&start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatchInline(` + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatchInline(` Object { "totalErrorGroups": 0, "totalErrorPages": 0, "totalErrors": 0, } `); - }); }); + }); - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_test_data'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_test_data'); - }); - + registry.when( + 'CSM JS errors without data', + { config: 'trial', archives: ['8.0.0', 'rum_test_data'] }, + () => { it('returns js errors', async () => { const response = await supertest.get( '/api/apm/rum-client/js-errors?start=2021-01-18T12%3A20%3A17.202Z&end=2021-01-18T12%3A25%3A17.203Z&uiFilters=%7B%22environment%22%3A%22ENVIRONMENT_ALL%22%2C%22serviceName%22%3A%5B%22elastic-co-frontend%22%5D%7D&pageSize=5&pageIndex=0' @@ -81,6 +74,6 @@ export default function rumJsErrorsApiTests({ getService }: FtrProviderContext) } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts b/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts similarity index 50% rename from x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts rename to x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts index 6db5de24baa99..0da0889c11775 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts +++ b/x-pack/test/apm_api_integration/tests/csm/long_task_metrics.ts @@ -5,38 +5,31 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('CSM long task metrics', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/long-task-metrics?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' - ); - - expect(response.status).to.be(200); - expect(response.body).to.eql({ - longestLongTask: 0, - noOfLongTasks: 0, - sumOfLongTasks: 0, - }); + registry.when('CSM long task metrics without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/long-task-metrics?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expect(response.body).to.eql({ + longestLongTask: 0, + noOfLongTasks: 0, + sumOfLongTasks: 0, }); }); + }); - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - + registry.when( + 'CSM long task metrics with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { it('returns web core vitals values', async () => { const response = await supertest.get( '/api/apm/rum-client/long-task-metrics?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' @@ -52,6 +45,6 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts b/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts new file mode 100644 index 0000000000000..28fa9cb87f516 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/csm/page_load_dist.ts @@ -0,0 +1,58 @@ +/* + * 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 '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + registry.when('UX page load dist without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + + it('returns empty list with breakdowns', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdown=Browser' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + }); + + registry.when( + 'UX page load dist with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { + it('returns page load distribution', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + it('returns page load distribution with breakdown', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdown=Browser' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/csm/page_views.ts b/x-pack/test/apm_api_integration/tests/csm/page_views.ts new file mode 100644 index 0000000000000..43f9d278f694a --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/csm/page_views.ts @@ -0,0 +1,58 @@ +/* + * 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 '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + registry.when('CSM page views without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + + it('returns empty list with breakdowns', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + }); + + registry.when( + 'CSM page views with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { + it('returns page views', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + it('returns page views with breakdown', async () => { + const response = await supertest.get( + '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatch(); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/csm/url_search.ts b/x-pack/test/apm_api_integration/tests/csm/url_search.ts new file mode 100644 index 0000000000000..906f1783cb90e --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/csm/url_search.ts @@ -0,0 +1,82 @@ +/* + * 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 '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function rumServicesApiTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + registry.when('CSM url search api without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&percentile=50' + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatchInline(` + Object { + "items": Array [], + "total": 0, + } + `); + }); + }); + + registry.when( + 'CSM url search api with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { + it('returns top urls when no query', async () => { + const response = await supertest.get( + '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&percentile=50' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatchInline(` + Object { + "items": Array [ + Object { + "count": 5, + "pld": 4924000, + "url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0", + }, + Object { + "count": 1, + "pld": 2760000, + "url": "http://localhost:5601/nfw/app/home", + }, + ], + "total": 2, + } + `); + }); + + it('returns specific results against query', async () => { + const response = await supertest.get( + '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&urlQuery=csm&percentile=50' + ); + + expect(response.status).to.be(200); + + expectSnapshot(response.body).toMatchInline(` + Object { + "items": Array [ + Object { + "count": 5, + "pld": 4924000, + "url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0", + }, + ], + "total": 1, + } + `); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts b/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts similarity index 55% rename from x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts rename to x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts index 50c261d2d37ad..3574ef065eef7 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/web_core_vitals.ts +++ b/x-pack/test/apm_api_integration/tests/csm/web_core_vitals.ts @@ -5,41 +5,34 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function rumServicesApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - describe('CSM web core vitals', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/web-core-vitals?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&percentile=50' - ); + registry.when('CSM web core vitals without data', { config: 'trial', archives: [] }, () => { + it('returns empty list', async () => { + const response = await supertest.get( + '/api/apm/rum-client/web-core-vitals?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&percentile=50' + ); - expect(response.status).to.be(200); - expect(response.body).to.eql({ - coreVitalPages: 0, - cls: null, - tbt: 0, - lcpRanks: [100, 0, 0], - fidRanks: [100, 0, 0], - clsRanks: [100, 0, 0], - }); + expect(response.status).to.be(200); + expect(response.body).to.eql({ + coreVitalPages: 0, + cls: null, + tbt: 0, + lcpRanks: [100, 0, 0], + fidRanks: [100, 0, 0], + clsRanks: [100, 0, 0], }); }); + }); - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - + registry.when( + 'CSM web core vitals with data', + { config: 'trial', archives: ['8.0.0', 'rum_8.0.0'] }, + () => { it('returns web core vitals values', async () => { const response = await supertest.get( '/api/apm/rum-client/web-core-vitals?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&percentile=50' @@ -73,6 +66,6 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts b/x-pack/test/apm_api_integration/tests/feature_controls.ts similarity index 98% rename from x-pack/test/apm_api_integration/basic/tests/feature_controls.ts rename to x-pack/test/apm_api_integration/tests/feature_controls.ts index 35025fcbfd107..7a65d8114c73f 100644 --- a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/tests/feature_controls.ts @@ -5,7 +5,8 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { FtrProviderContext } from '../common/ftr_provider_context'; +import { registry } from '../common/registry'; export default function featureControlsTests({ getService }: FtrProviderContext) { const supertest = getService('supertestAsApmWriteUser'); @@ -270,7 +271,7 @@ export default function featureControlsTests({ getService }: FtrProviderContext) } } - describe('apm feature controls', () => { + registry.when('apm feature controls', { config: 'basic', archives: [] }, () => { const config = { service: { name: 'test-service' }, settings: { transaction_sample_rate: '0.5' }, diff --git a/x-pack/test/apm_api_integration/tests/index.ts b/x-pack/test/apm_api_integration/tests/index.ts new file mode 100644 index 0000000000000..eef82c714b2d0 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/index.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../common/ftr_provider_context'; +import { registry } from '../common/registry'; + +export default function apmApiIntegrationTests(providerContext: FtrProviderContext) { + const { loadTestFile } = providerContext; + + describe('APM API tests', function () { + this.tags('ciGroup1'); + loadTestFile(require.resolve('./alerts/chart_preview')); + + loadTestFile(require.resolve('./correlations/slow_transactions')); + + loadTestFile(require.resolve('./csm/csm_services')); + loadTestFile(require.resolve('./csm/has_rum_data')); + loadTestFile(require.resolve('./csm/js_errors')); + loadTestFile(require.resolve('./csm/long_task_metrics')); + loadTestFile(require.resolve('./csm/page_load_dist')); + loadTestFile(require.resolve('./csm/page_views')); + loadTestFile(require.resolve('./csm/url_search')); + loadTestFile(require.resolve('./csm/web_core_vitals')); + + loadTestFile(require.resolve('./metrics_charts/metrics_charts')); + + loadTestFile(require.resolve('./observability_overview/has_data')); + loadTestFile(require.resolve('./observability_overview/observability_overview')); + + loadTestFile(require.resolve('./service_maps/service_maps')); + + loadTestFile(require.resolve('./service_overview/dependencies')); + loadTestFile(require.resolve('./service_overview/error_groups')); + loadTestFile(require.resolve('./service_overview/instances')); + + loadTestFile(require.resolve('./services/agent_name')); + loadTestFile(require.resolve('./services/annotations')); + loadTestFile(require.resolve('./services/service_details')); + loadTestFile(require.resolve('./services/service_icons')); + loadTestFile(require.resolve('./services/throughput')); + loadTestFile(require.resolve('./services/top_services')); + loadTestFile(require.resolve('./services/transaction_types')); + + loadTestFile(require.resolve('./settings/anomaly_detection/basic')); + loadTestFile(require.resolve('./settings/anomaly_detection/no_access_user')); + loadTestFile(require.resolve('./settings/anomaly_detection/read_user')); + loadTestFile(require.resolve('./settings/anomaly_detection/write_user')); + + loadTestFile(require.resolve('./settings/agent_configuration')); + + loadTestFile(require.resolve('./settings/custom_link')); + + loadTestFile(require.resolve('./traces/top_traces')); + + loadTestFile(require.resolve('./transactions/breakdown')); + loadTestFile(require.resolve('./transactions/distribution')); + loadTestFile(require.resolve('./transactions/error_rate')); + loadTestFile(require.resolve('./transactions/latency')); + loadTestFile(require.resolve('./transactions/throughput')); + loadTestFile(require.resolve('./transactions/top_transaction_groups')); + loadTestFile(require.resolve('./transactions/transactions_groups_overview')); + + loadTestFile(require.resolve('./feature_controls')); + + registry.run(providerContext); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts new file mode 100644 index 0000000000000..dda46f00d7c72 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/metrics_charts/metrics_charts.ts @@ -0,0 +1,446 @@ +/* + * 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 { first } from 'lodash'; +import { MetricsChartsByAgentAPIResponse } from '../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; +import { GenericMetricsChart } from '../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +interface ChartResponse { + body: MetricsChartsByAgentAPIResponse; + status: number; +} + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + registry.when( + 'Metrics charts when data is loaded', + { config: 'basic', archives: ['metrics_8.0.0'] }, + () => { + describe('for opbeans-node', () => { + const start = encodeURIComponent('2020-09-08T14:50:00.000Z'); + const end = encodeURIComponent('2020-09-08T14:55:00.000Z'); + const uiFilters = encodeURIComponent(JSON.stringify({})); + const agentName = 'nodejs'; + + describe('returns metrics data', () => { + let chartsResponse: ChartResponse; + before(async () => { + chartsResponse = await supertest.get( + `/api/apm/services/opbeans-node/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + ); + }); + it('contains CPU usage and System memory usage chart data', async () => { + expect(chartsResponse.status).to.be(200); + expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` + Array [ + "CPU usage", + "System memory usage", + ] + `); + }); + + describe('CPU usage', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'cpu_usage_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "System max", + "System average", + "Process max", + "Process average", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0.714, + 0.3877, + 0.75, + 0.2543, + ] + `); + }); + }); + + describe("System memory usage (using 'system.memory' fields to calculate the memory usage)", () => { + let systemMemoryUsageChart: GenericMetricsChart | undefined; + before(() => { + systemMemoryUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'memory_usage_chart' + ); + }); + + it('has correct series', () => { + expect(systemMemoryUsageChart).to.not.empty(); + expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)) + .toMatchInline(` + Array [ + "Max", + "Average", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0.722093920925555, + 0.718173546796348, + ] + `); + }); + }); + }); + }); + + describe('for opbeans-java', () => { + const uiFilters = encodeURIComponent(JSON.stringify({})); + const agentName = 'java'; + + describe('returns metrics data', () => { + const start = encodeURIComponent('2020-09-08T14:55:30.000Z'); + const end = encodeURIComponent('2020-09-08T15:00:00.000Z'); + + let chartsResponse: ChartResponse; + before(async () => { + chartsResponse = await supertest.get( + `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + ); + }); + + it('has correct chart data', async () => { + expect(chartsResponse.status).to.be(200); + expectSnapshot(chartsResponse.body.charts.map((chart) => chart.title)).toMatchInline(` + Array [ + "CPU usage", + "System memory usage", + "Heap Memory", + "Non-Heap Memory", + "Thread Count", + "Garbage collection per minute", + "Garbage collection time spent per minute", + ] + `); + }); + + describe('CPU usage', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'cpu_usage_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "System max", + "System average", + "Process max", + "Process average", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0.203, + 0.178777777777778, + 0.01, + 0.009, + ] + `); + }); + + it('has the correct rate', async () => { + const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 0.193, + 0.193, + 0.009, + 0.009, + ] + `); + }); + }); + + describe("System memory usage (using 'system.process.cgroup' fields to calculate the memory usage)", () => { + let systemMemoryUsageChart: GenericMetricsChart | undefined; + before(() => { + systemMemoryUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'memory_usage_chart' + ); + }); + + it('has correct series', () => { + expect(systemMemoryUsageChart).to.not.empty(); + expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)) + .toMatchInline(` + Array [ + "Max", + "Average", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0.707924703557837, + 0.705395980841182, + ] + `); + }); + + it('has the correct rate', async () => { + const yValues = systemMemoryUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 0.707924703557837, + 0.707924703557837, + ] + `); + }); + }); + + describe('Heap Memory', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'heap_memory_area_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "Avg. used", + "Avg. committed", + "Avg. limit", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 222501617.777778, + 374341632, + 1560281088, + ] + `); + }); + + it('has the correct rate', async () => { + const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 211472896, + 374341632, + 1560281088, + ] + `); + }); + }); + + describe('Non-Heap Memory', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'non_heap_memory_area_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "Avg. used", + "Avg. committed", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 138573397.333333, + 147677639.111111, + ] + `); + }); + + it('has the correct rate', async () => { + const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 138162752, + 147386368, + ] + `); + }); + }); + + describe('Thread Count', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'thread_count_line_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "Avg. count", + "Max count", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 44.4444444444444, + 45, + ] + `); + }); + + it('has the correct rate', async () => { + const yValues = cpuUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 44, + 44, + ] + `); + }); + }); + + describe('Garbage collection per minute', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'gc_rate_line_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "G1 Old Generation", + "G1 Young Generation", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0, + 15, + ] + `); + }); + }); + + describe('Garbage collection time spent per minute', () => { + let cpuUsageChart: GenericMetricsChart | undefined; + before(() => { + cpuUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'gc_time_line_chart' + ); + }); + + it('has correct series', () => { + expect(cpuUsageChart).to.not.empty(); + expectSnapshot(cpuUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "G1 Old Generation", + "G1 Young Generation", + ] + `); + }); + + it('has correct series overall values', () => { + expectSnapshot(cpuUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0, + 187.5, + ] + `); + }); + }); + }); + + // 9223372036854771712 = memory limit for a c-group when no memory limit is specified + it('calculates system memory usage using system total field when cgroup limit is equal to 9223372036854771712', async () => { + const start = encodeURIComponent('2020-09-08T15:00:30.000Z'); + const end = encodeURIComponent('2020-09-08T15:05:00.000Z'); + + const chartsResponse: ChartResponse = await supertest.get( + `/api/apm/services/opbeans-java/metrics/charts?start=${start}&end=${end}&uiFilters=${uiFilters}&agentName=${agentName}` + ); + + const systemMemoryUsageChart = chartsResponse.body.charts.find( + ({ key }) => key === 'memory_usage_chart' + ); + + expect(systemMemoryUsageChart).to.not.empty(); + expectSnapshot(systemMemoryUsageChart?.series.map(({ title }) => title)).toMatchInline(` + Array [ + "Max", + "Average", + ] + `); + expectSnapshot(systemMemoryUsageChart?.series.map(({ overallValue }) => overallValue)) + .toMatchInline(` + Array [ + 0.114523896426499, + 0.114002376090415, + ] + `); + + const yValues = systemMemoryUsageChart?.series.map((serie) => first(serie.data)?.y); + expectSnapshot(yValues).toMatchInline(` + Array [ + 0.11383724014064, + 0.11383724014064, + ] + `); + }); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/observability_overview/has_data.ts b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts similarity index 67% rename from x-pack/test/apm_api_integration/basic/tests/observability_overview/has_data.ts rename to x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts index 6d0d2d3042625..9c88f75c6adbd 100644 --- a/x-pack/test/apm_api_integration/basic/tests/observability_overview/has_data.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/has_data.ts @@ -4,40 +4,45 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - const archiveName = 'apm_8.0.0'; - - describe('Has data', () => { - describe('when data is not loaded', () => { + registry.when( + 'Observability overview when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('returns false when there is no data', async () => { const response = await supertest.get('/api/apm/observability_overview/has_data'); expect(response.status).to.be(200); expect(response.body).to.eql(false); }); - }); - describe('when only onboarding data is loaded', () => { - before(() => esArchiver.load('observability_overview')); - after(() => esArchiver.unload('observability_overview')); + } + ); + + registry.when( + 'Observability overview when only onboarding data is loaded', + { config: 'basic', archives: ['observability_overview'] }, + () => { it('returns false when there is only onboarding data', async () => { const response = await supertest.get('/api/apm/observability_overview/has_data'); expect(response.status).to.be(200); expect(response.body).to.eql(false); }); - }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Observability overview when APM data is loaded', + { config: 'basic', archives: ['apm_8.0.0'] }, + () => { it('returns true when there is at least one document on transaction, error or metrics indices', async () => { const response = await supertest.get('/api/apm/observability_overview/has_data'); expect(response.status).to.be(200); expect(response.body).to.eql(true); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts similarity index 69% rename from x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts rename to x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts index f7c459029c7f5..7c9d8fa8b0f91 100644 --- a/x-pack/test/apm_api_integration/basic/tests/observability_overview/observability_overview.ts +++ b/x-pack/test/apm_api_integration/tests/observability_overview/observability_overview.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import expect from '@kbn/expect'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; 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]; @@ -19,22 +19,28 @@ export default function ApiTest({ getService }: FtrProviderContext) { const end = encodeURIComponent(metadata.end); const bucketSize = '60s'; - describe('Observability overview', () => { - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` - ); - expect(response.status).to.be(200); + registry.when( + 'Observability overview when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` + ); + expect(response.status).to.be(200); - expect(response.body.serviceCount).to.be(0); - expect(response.body.transactionCoordinates.length).to.be(0); + expect(response.body.serviceCount).to.be(0); + expect(response.body.transactionCoordinates.length).to.be(0); + }); }); - }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Observability overview when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns the service count and transaction coordinates', async () => { const response = await supertest.get( `/api/apm/observability_overview?start=${start}&end=${end}&bucketSize=${bucketSize}` @@ -80,6 +86,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { ] `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap b/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap similarity index 99% rename from x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap rename to x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap index 7639822eaa6f9..69bc039b67ed2 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/__snapshots__/service_maps.snap +++ b/x-pack/test/apm_api_integration/tests/service_maps/__snapshots__/service_maps.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (trial) Service Maps Service Maps with a trial license /api/apm/service-map when there is data returns service map elements filtering by environment not defined 1`] = ` +exports[`APM API tests trial apm_8.0.0 Service Map with data /api/apm/service-map returns service map elements filtering by environment not defined 1`] = ` Object { "elements": Array [ Object { @@ -514,7 +514,7 @@ Object { } `; -exports[`APM specs (trial) Service Maps Service Maps with a trial license /api/apm/service-map when there is data returns the correct data 3`] = ` +exports[`APM API tests trial apm_8.0.0 Service Map with data /api/apm/service-map returns the correct data 3`] = ` Array [ Object { "data": Object { @@ -1741,7 +1741,7 @@ Array [ ] `; -exports[`APM specs (trial) Service Maps Service Maps with a trial license when there is data with anomalies with the default apm user returns the correct anomaly stats 3`] = ` +exports[`APM API tests trial apm_8.0.0 Service Map with data /api/apm/service-map with ML data with the default apm user returns the correct anomaly stats 3`] = ` Object { "elements": Array [ Object { diff --git a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts similarity index 58% rename from x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts rename to x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts index 02acd34ad5666..a15f0442c7cde 100644 --- a/x-pack/test/apm_api_integration/trial/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts @@ -7,57 +7,85 @@ import querystring from 'querystring'; import expect from '@kbn/expect'; import { isEmpty, uniq } from 'lodash'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function serviceMapsApiTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestAsApmReadUserWithoutMlAccess = getService('supertestAsApmReadUserWithoutMlAccess'); - 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); - describe('Service Maps with a trial license', () => { + registry.when('Service map with a basic license', { config: 'basic', archives: [] }, () => { + it('is only be available to users with Platinum license (or higher)', async () => { + const response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); + + expect(response.status).to.be(403); + + expectSnapshot(response.body.message).toMatchInline( + `"In order to access Service Maps, you must be subscribed to an Elastic Platinum license. With it, you'll have the ability to visualize your entire application stack along with your APM data."` + ); + }); + }); + + registry.when('Service map without data', { config: 'trial', archives: [] }, () => { describe('/api/apm/service-map', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); + it('returns an empty list', async () => { + const response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); - expect(response.status).to.be(200); - expect(response.body.elements.length).to.be(0); - }); + expect(response.status).to.be(200); + expect(response.body.elements.length).to.be(0); }); + }); - describe('when there is data', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + describe('/api/apm/service-map/service/{serviceName}', () => { + it('returns an object with nulls', async () => { + const q = querystring.stringify({ + start: metadata.start, + end: metadata.end, + uiFilters: encodeURIComponent('{}'), + }); + const response = await supertest.get(`/api/apm/service-map/service/opbeans-node?${q}`); - let response: PromiseReturnType; + expect(response.status).to.be(200); - before(async () => { - response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); - }); + expect(response.body.avgCpuUsage).to.be(null); + expect(response.body.avgErrorRate).to.be(null); + expect(response.body.avgMemoryUsage).to.be(null); + expect(response.body.transactionStats.avgRequestsPerMinute).to.be(null); + expect(response.body.transactionStats.avgTransactionDuration).to.be(null); + }); + }); + }); - it('returns service map elements', () => { - expect(response.status).to.be(200); - expect(response.body.elements.length).to.be.greaterThan(0); - }); + registry.when('Service Map with data', { config: 'trial', archives: ['apm_8.0.0'] }, () => { + describe('/api/apm/service-map', () => { + let response: PromiseReturnType; + + before(async () => { + response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); + }); + + it('returns service map elements', () => { + expect(response.status).to.be(200); + expect(response.body.elements.length).to.be.greaterThan(0); + }); - it('returns the correct data', () => { - const elements: Array<{ data: Record }> = response.body.elements; + it('returns the correct data', () => { + const elements: Array<{ data: Record }> = response.body.elements; - const serviceNames = uniq( - elements - .filter((element) => element.data['service.name'] !== undefined) - .map((element) => element.data['service.name']) - ).sort(); + const serviceNames = uniq( + elements + .filter((element) => element.data['service.name'] !== undefined) + .map((element) => element.data['service.name']) + ).sort(); - expectSnapshot(serviceNames).toMatchInline(` + expectSnapshot(serviceNames).toMatchInline(` Array [ "kibana", "kibana-frontend", @@ -71,13 +99,13 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) ] `); - const externalDestinations = uniq( - elements - .filter((element) => element.data.target?.startsWith('>')) - .map((element) => element.data.target) - ).sort(); + const externalDestinations = uniq( + elements + .filter((element) => element.data.target?.startsWith('>')) + .map((element) => element.data.target) + ).sort(); - expectSnapshot(externalDestinations).toMatchInline(` + expectSnapshot(externalDestinations).toMatchInline(` Array [ ">elasticsearch", ">feeds.elastic.co:443", @@ -86,51 +114,26 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) ] `); - expectSnapshot(elements).toMatch(); - }); - - it('returns service map elements filtering by environment not defined', async () => { - const ENVIRONMENT_NOT_DEFINED = 'ENVIRONMENT_NOT_DEFINED'; - const { body, status } = await supertest.get( - `/api/apm/service-map?start=${start}&end=${end}&environment=${ENVIRONMENT_NOT_DEFINED}` - ); - expect(status).to.be(200); - const environments = new Set(); - body.elements.forEach((element: { data: Record }) => { - environments.add(element.data['service.environment']); - }); - - expect(environments.has(ENVIRONMENT_NOT_DEFINED)).to.eql(true); - expectSnapshot(body).toMatch(); - }); + expectSnapshot(elements).toMatch(); }); - }); - - describe('/api/apm/service-map/service/{serviceName}', () => { - describe('when there is no data', () => { - it('returns an object with nulls', async () => { - const q = querystring.stringify({ - start: metadata.start, - end: metadata.end, - uiFilters: encodeURIComponent('{}'), - }); - const response = await supertest.get(`/api/apm/service-map/service/opbeans-node?${q}`); - expect(response.status).to.be(200); - - expect(response.body.avgCpuUsage).to.be(null); - expect(response.body.avgErrorRate).to.be(null); - expect(response.body.avgMemoryUsage).to.be(null); - expect(response.body.transactionStats.avgRequestsPerMinute).to.be(null); - expect(response.body.transactionStats.avgTransactionDuration).to.be(null); + it('returns service map elements filtering by environment not defined', async () => { + const ENVIRONMENT_NOT_DEFINED = 'ENVIRONMENT_NOT_DEFINED'; + const { body, status } = await supertest.get( + `/api/apm/service-map?start=${start}&end=${end}&environment=${ENVIRONMENT_NOT_DEFINED}` + ); + expect(status).to.be(200); + const environments = new Set(); + body.elements.forEach((element: { data: Record }) => { + environments.add(element.data['service.environment']); }); + + expect(environments.has(ENVIRONMENT_NOT_DEFINED)).to.eql(true); + expectSnapshot(body).toMatch(); }); }); - describe('when there is data with anomalies', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - + describe('/api/apm/service-map with ML data', () => { describe('with the default apm user', () => { let response: PromiseReturnType; diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/dependencies/es_utils.ts b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/es_utils.ts similarity index 100% rename from x-pack/test/apm_api_integration/basic/tests/service_overview/dependencies/es_utils.ts rename to x-pack/test/apm_api_integration/tests/service_overview/dependencies/es_utils.ts diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/dependencies/index.ts b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts similarity index 91% rename from x-pack/test/apm_api_integration/basic/tests/service_overview/dependencies/index.ts rename to x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts index aeb5d1256796a..b3e7e0672fc7f 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_overview/dependencies/index.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/dependencies/index.ts @@ -8,26 +8,28 @@ import expect from '@kbn/expect'; import url from 'url'; import { sortBy, pick, last } from 'lodash'; import { ValuesType } from 'utility-types'; -import { Maybe } from '../../../../../../plugins/apm/typings/common'; -import { isFiniteNumber } from '../../../../../../plugins/apm/common/utils/is_finite_number'; -import { APIReturnType } from '../../../../../../plugins/apm/public/services/rest/createCallApmApi'; -import { ENVIRONMENT_ALL } from '../../../../../../plugins/apm/common/environment_filter_values'; -import { FtrProviderContext } from '../../../../../common/ftr_provider_context'; -import archives from '../../../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../../common/registry'; +import { Maybe } from '../../../../../plugins/apm/typings/common'; +import { isFiniteNumber } from '../../../../../plugins/apm/common/utils/is_finite_number'; +import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; +import { ENVIRONMENT_ALL } from '../../../../../plugins/apm/common/environment_filter_values'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives from '../../../common/fixtures/es_archiver/archives_metadata'; import { apmDependenciesMapping, createServiceDependencyDocs } from './es_utils'; const round = (num: Maybe): string => (isFiniteNumber(num) ? num.toPrecision(4) : ''); export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const es = getService('es'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; - describe('Service overview dependencies', () => { - describe('when data is not loaded', () => { + registry.when( + 'Service overview dependencies when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles the empty state', async () => { const response = await supertest.get( url.format({ @@ -44,9 +46,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(response.body).to.eql([]); }); - }); + } + ); - describe('when specific data is loaded', () => { + registry.when( + 'Service overview dependencies when specific data is loaded', + { config: 'basic', archives: [] }, + () => { let response: { status: number; body: APIReturnType<'GET /api/apm/services/{serviceName}/dependencies'>; @@ -285,17 +291,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { impact: 0, }); }); - }); + } + ); - describe('when data is loaded', () => { + registry.when( + 'Service overview dependencies when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { let response: { status: number; body: APIReturnType<'GET /api/apm/services/{serviceName}/dependencies'>; }; before(async () => { - await esArchiver.load(archiveName); - response = await supertest.get( url.format({ pathname: `/api/apm/services/opbeans-java/dependencies`, @@ -309,8 +317,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { ); }); - after(() => esArchiver.unload(archiveName)); - it('returns a successful response', () => { expect(response.status).to.be(200); }); @@ -380,6 +386,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { ] `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts b/x-pack/test/apm_api_integration/tests/service_overview/error_groups.ts similarity index 94% rename from x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts rename to x-pack/test/apm_api_integration/tests/service_overview/error_groups.ts index 7d1c05960f3e6..fc649c60103c8 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_overview/error_groups.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/error_groups.ts @@ -7,18 +7,20 @@ import expect from '@kbn/expect'; import qs from 'querystring'; import { pick, uniqBy } from 'lodash'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; - describe('Service overview error groups', () => { - describe('when data is not loaded', () => { + registry.when( + 'Service overview error groups when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles the empty state', async () => { const response = await supertest.get( `/api/apm/services/opbeans-java/error_groups?${qs.stringify({ @@ -41,12 +43,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { is_aggregation_accurate: true, }); }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Service overview error groups when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns the correct data', async () => { const response = await supertest.get( `/api/apm/services/opbeans-java/error_groups?${qs.stringify({ @@ -220,6 +223,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(uniqBy(items, 'group_id').length).to.eql(totalItems); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts b/x-pack/test/apm_api_integration/tests/service_overview/instances.ts similarity index 83% rename from x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts rename to x-pack/test/apm_api_integration/tests/service_overview/instances.ts index 2227a8c09a6cf..08d60f90900b8 100644 --- a/x-pack/test/apm_api_integration/basic/tests/service_overview/instances.ts +++ b/x-pack/test/apm_api_integration/tests/service_overview/instances.ts @@ -7,14 +7,14 @@ import expect from '@kbn/expect'; import url from 'url'; import { pick, sortBy } from 'lodash'; -import { isFiniteNumber } from '../../../../../plugins/apm/common/utils/is_finite_number'; -import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { isFiniteNumber } from '../../../../plugins/apm/common/utils/is_finite_number'; +import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; @@ -24,31 +24,36 @@ export default function ApiTest({ getService }: FtrProviderContext) { body: APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances'>; } - describe('Service overview instances', () => { - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response: Response = await supertest.get( - url.format({ - pathname: `/api/apm/services/opbeans-java/service_overview_instances`, - query: { - start, - end, - numBuckets: 20, - transactionType: 'request', - uiFilters: '{}', - }, - }) - ); - - expect(response.status).to.be(200); - expect(response.body).to.eql([]); - }); - }); + registry.when( + 'Service overview instances when data is not loaded', + { config: 'basic', archives: [] }, + () => { + describe('when data is not loaded', () => { + it('handles the empty state', async () => { + const response: Response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-java/service_overview_instances`, + query: { + start, + end, + numBuckets: 20, + transactionType: 'request', + uiFilters: '{}', + }, + }) + ); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + expect(response.status).to.be(200); + expect(response.body).to.eql([]); + }); + }); + } + ); + registry.when( + 'Service overview instances when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { describe('fetching java data', () => { let response: Response; @@ -209,6 +214,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expectSnapshot(values); }); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap b/x-pack/test/apm_api_integration/tests/services/__snapshots__/throughput.snap similarity index 96% rename from x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap rename to x-pack/test/apm_api_integration/tests/services/__snapshots__/throughput.snap index fe7f434aad2e1..f23601fccb174 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/__snapshots__/throughput.snap +++ b/x-pack/test/apm_api_integration/tests/services/__snapshots__/throughput.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (basic) Services Throughput when data is loaded returns the service throughput has the correct throughput 1`] = ` +exports[`APM API tests basic apm_8.0.0 Throughput when data is loaded has the correct throughput 1`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts b/x-pack/test/apm_api_integration/tests/services/agent_name.ts similarity index 55% rename from x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts rename to x-pack/test/apm_api_integration/tests/services/agent_name.ts index cea8fb5da2428..538d7a448ea30 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/agent_name.ts +++ b/x-pack/test/apm_api_integration/tests/services/agent_name.ts @@ -5,34 +5,33 @@ */ import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const range = archives[archiveName]; const start = encodeURIComponent(range.start); const end = encodeURIComponent(range.end); - describe('Agent name', () => { - describe('when data is not loaded ', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - `/api/apm/services/opbeans-node/agent_name?start=${start}&end=${end}` - ); + registry.when('Agent name when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/agent_name?start=${start}&end=${end}` + ); - expect(response.status).to.be(200); - expect(response.body).to.eql({}); - }); + expect(response.status).to.be(200); + expect(response.body).to.eql({}); }); + }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - + registry.when( + 'Agent name when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns the agent name', async () => { const response = await supertest.get( `/api/apm/services/opbeans-node/agent_name?start=${start}&end=${end}` @@ -42,6 +41,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body).to.eql({ agentName: 'nodejs' }); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/trial/tests/services/annotations.ts b/x-pack/test/apm_api_integration/tests/services/annotations.ts similarity index 92% rename from x-pack/test/apm_api_integration/trial/tests/services/annotations.ts rename to x-pack/test/apm_api_integration/tests/services/annotations.ts index c2ddc10c5f1d2..4ff690fa01aa1 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/annotations.ts +++ b/x-pack/test/apm_api_integration/tests/services/annotations.ts @@ -7,7 +7,8 @@ import expect from '@kbn/expect'; import { merge, cloneDeep, isPlainObject } from 'lodash'; import { JsonObject } from 'src/plugins/kibana_utils/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; const DEFAULT_INDEX_NAME = 'observability-annotations'; @@ -40,7 +41,32 @@ export default function annotationApiTests({ getService }: FtrProviderContext) { } } - describe('APM annotations with a trial license', () => { + registry.when('Annotations with a basic license', { config: 'basic', archives: [] }, () => { + describe('when creating an annotation', () => { + it('fails with a 403 forbidden', async () => { + const response = await request({ + url: '/api/apm/services/opbeans-java/annotation', + method: 'POST', + data: { + '@timestamp': new Date().toISOString(), + message: 'New deployment', + tags: ['foo'], + service: { + version: '1.1', + environment: 'production', + }, + }, + }); + + expect(response.status).to.be(403); + expect(response.body.message).to.be( + 'Annotations require at least a gold license or a trial license.' + ); + }); + }); + }); + + registry.when('Annotations with a trial license', { config: 'trial', archives: [] }, () => { describe('when creating an annotation', () => { afterEach(async () => { const indexExists = (await es.indices.exists({ index: DEFAULT_INDEX_NAME })).body; diff --git a/x-pack/test/apm_api_integration/basic/tests/services/service_details.ts b/x-pack/test/apm_api_integration/tests/services/service_details.ts similarity index 87% rename from x-pack/test/apm_api_integration/basic/tests/services/service_details.ts rename to x-pack/test/apm_api_integration/tests/services/service_details.ts index 54bd16e6f78c4..77155c907f3b1 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/service_details.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_details.ts @@ -6,18 +6,20 @@ import expect from '@kbn/expect'; import url from 'url'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; - describe('Service details', () => { - describe('when data is not loaded ', () => { + registry.when( + 'Service details when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles the empty state', async () => { const response = await supertest.get( url.format({ @@ -29,12 +31,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expect(response.body).to.eql({}); }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Service details when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns java service details', async () => { const response = await supertest.get( url.format({ @@ -116,6 +119,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/services/service_icons.ts b/x-pack/test/apm_api_integration/tests/services/service_icons.ts similarity index 53% rename from x-pack/test/apm_api_integration/basic/tests/services/service_icons.ts rename to x-pack/test/apm_api_integration/tests/services/service_icons.ts index 4b79de14551d6..7926a2744e45c 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/service_icons.ts +++ b/x-pack/test/apm_api_integration/tests/services/service_icons.ts @@ -6,35 +6,34 @@ import expect from '@kbn/expect'; import url from 'url'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; - describe('Service icons', () => { - describe('when data is not loaded ', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - url.format({ - pathname: `/api/apm/services/opbeans-java/metadata/icons`, - query: { start, end }, - }) - ); + registry.when('Service icons when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-java/metadata/icons`, + query: { start, end }, + }) + ); - expect(response.status).to.be(200); - expect(response.body).to.eql({}); - }); + expect(response.status).to.be(200); + expect(response.body).to.eql({}); }); + }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - + registry.when( + 'Service icons when data is not loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns java service icons', async () => { const response = await supertest.get( url.format({ @@ -46,11 +45,11 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expectSnapshot(response.body).toMatchInline(` - Object { - "agentName": "java", - "containerType": "Kubernetes", - } - `); + Object { + "agentName": "java", + "containerType": "Kubernetes", + } + `); }); it('returns python service icons', async () => { @@ -64,13 +63,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.status).to.be(200); expectSnapshot(response.body).toMatchInline(` - Object { - "agentName": "python", - "cloudProvider": "gcp", - "containerType": "Kubernetes", - } - `); + Object { + "agentName": "python", + "cloudProvider": "gcp", + "containerType": "Kubernetes", + } + `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/tests/services/throughput.ts b/x-pack/test/apm_api_integration/tests/services/throughput.ts new file mode 100644 index 0000000000000..fa94cbf600709 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/services/throughput.ts @@ -0,0 +1,82 @@ +/* + * 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 qs from 'querystring'; +import { first, last } from 'lodash'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const archiveName = 'apm_8.0.0'; + const metadata = archives_metadata[archiveName]; + + registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-java/throughput?${qs.stringify({ + start: metadata.start, + end: metadata.end, + uiFilters: encodeURIComponent('{}'), + transactionType: 'request', + })}` + ); + expect(response.status).to.be(200); + expect(response.body.throughput.length).to.be(0); + }); + }); + + registry.when( + 'Throughput when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { + let throughputResponse: { + throughput: Array<{ x: number; y: number | null }>; + }; + before(async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-java/throughput?${qs.stringify({ + start: metadata.start, + end: metadata.end, + uiFilters: encodeURIComponent('{}'), + transactionType: 'request', + })}` + ); + throughputResponse = response.body; + }); + + it('returns some data', () => { + expect(throughputResponse.throughput.length).to.be.greaterThan(0); + + const nonNullDataPoints = throughputResponse.throughput.filter(({ y }) => y !== null); + + expect(nonNullDataPoints.length).to.be.greaterThan(0); + }); + + it('has the correct start date', () => { + expectSnapshot( + new Date(first(throughputResponse.throughput)?.x ?? NaN).toISOString() + ).toMatchInline(`"2020-12-08T13:57:30.000Z"`); + }); + + it('has the correct end date', () => { + expectSnapshot( + new Date(last(throughputResponse.throughput)?.x ?? NaN).toISOString() + ).toMatchInline(`"2020-12-08T14:27:30.000Z"`); + }); + + it('has the correct number of buckets', () => { + expectSnapshot(throughputResponse.throughput.length).toMatchInline(`61`); + }); + + it('has the correct throughput', () => { + expectSnapshot(throughputResponse.throughput).toMatch(); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/tests/services/top_services.ts b/x-pack/test/apm_api_integration/tests/services/top_services.ts new file mode 100644 index 0000000000000..42797e3e7c87a --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/services/top_services.ts @@ -0,0 +1,364 @@ +/* + * 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 { sortBy, pick, isEmpty } from 'lodash'; +import { APIReturnType } from '../../../../plugins/apm/public/services/rest/createCallApmApi'; +import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const supertestAsApmReadUserWithoutMlAccess = getService('supertestAsApmReadUserWithoutMlAccess'); + + const archiveName = 'apm_8.0.0'; + + const range = archives_metadata[archiveName]; + + // url parameters + const start = encodeURIComponent(range.start); + const end = encodeURIComponent(range.end); + + const uiFilters = encodeURIComponent(JSON.stringify({})); + + registry.when( + 'APM Services Overview with a basic license when data is not loaded', + { config: 'basic', archives: [] }, + () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + + expect(response.status).to.be(200); + expect(response.body.hasHistoricalData).to.be(false); + expect(response.body.hasLegacyData).to.be(false); + expect(response.body.items.length).to.be(0); + }); + } + ); + + registry.when( + 'APM Services Overview with a basic license when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { + let response: { + status: number; + body: APIReturnType<'GET /api/apm/services'>; + }; + + let sortedItems: typeof response.body.items; + + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + sortedItems = sortBy(response.body.items, 'serviceName'); + }); + + it('the response is successful', () => { + expect(response.status).to.eql(200); + }); + + it('returns hasHistoricalData: true', () => { + expect(response.body.hasHistoricalData).to.be(true); + }); + + it('returns hasLegacyData: false', () => { + expect(response.body.hasLegacyData).to.be(false); + }); + + it('returns the correct service names', () => { + expectSnapshot(sortedItems.map((item) => item.serviceName)).toMatchInline(` + Array [ + "kibana", + "kibana-frontend", + "opbeans-dotnet", + "opbeans-go", + "opbeans-java", + "opbeans-node", + "opbeans-python", + "opbeans-ruby", + "opbeans-rum", + ] + `); + }); + + it('returns the correct metrics averages', () => { + expectSnapshot( + sortedItems.map((item) => + pick( + item, + 'transactionErrorRate.value', + 'avgResponseTime.value', + 'transactionsPerMinute.value' + ) + ) + ).toMatchInline(` + Array [ + Object { + "avgResponseTime": Object { + "value": 420419.34550767, + }, + "transactionErrorRate": Object { + "value": 0, + }, + "transactionsPerMinute": Object { + "value": 45.6333333333333, + }, + }, + Object { + "avgResponseTime": Object { + "value": 2382833.33333333, + }, + "transactionErrorRate": Object { + "value": null, + }, + "transactionsPerMinute": Object { + "value": 0.2, + }, + }, + Object { + "avgResponseTime": Object { + "value": 631521.83908046, + }, + "transactionErrorRate": Object { + "value": 0.0229885057471264, + }, + "transactionsPerMinute": Object { + "value": 2.9, + }, + }, + Object { + "avgResponseTime": Object { + "value": 27946.1484375, + }, + "transactionErrorRate": Object { + "value": 0.015625, + }, + "transactionsPerMinute": Object { + "value": 4.26666666666667, + }, + }, + Object { + "avgResponseTime": Object { + "value": 237339.813333333, + }, + "transactionErrorRate": Object { + "value": 0.16, + }, + "transactionsPerMinute": Object { + "value": 2.5, + }, + }, + Object { + "avgResponseTime": Object { + "value": 24920.1052631579, + }, + "transactionErrorRate": Object { + "value": 0.0210526315789474, + }, + "transactionsPerMinute": Object { + "value": 3.16666666666667, + }, + }, + Object { + "avgResponseTime": Object { + "value": 29542.6607142857, + }, + "transactionErrorRate": Object { + "value": 0.0357142857142857, + }, + "transactionsPerMinute": Object { + "value": 1.86666666666667, + }, + }, + Object { + "avgResponseTime": Object { + "value": 70518.9328358209, + }, + "transactionErrorRate": Object { + "value": 0.0373134328358209, + }, + "transactionsPerMinute": Object { + "value": 4.46666666666667, + }, + }, + Object { + "avgResponseTime": Object { + "value": 2319812.5, + }, + "transactionErrorRate": Object { + "value": null, + }, + "transactionsPerMinute": Object { + "value": 0.533333333333333, + }, + }, + ] + `); + }); + + it('returns environments', () => { + expectSnapshot(sortedItems.map((item) => item.environments ?? [])).toMatchInline(` + Array [ + Array [ + "production", + ], + Array [ + "production", + ], + Array [ + "production", + ], + Array [ + "testing", + ], + Array [ + "production", + ], + Array [ + "testing", + ], + Array [], + Array [], + Array [ + "testing", + ], + ] + `); + }); + + it(`RUM services don't report any transaction error rates`, () => { + // RUM transactions don't have event.outcome set, + // so they should not have an error rate + + const rumServices = sortedItems.filter((item) => item.agentName === 'rum-js'); + + expect(rumServices.length).to.be.greaterThan(0); + + expect(rumServices.every((item) => isEmpty(item.transactionErrorRate?.value))); + }); + + it('non-RUM services all report transaction error rates', () => { + const nonRumServices = sortedItems.filter((item) => item.agentName !== 'rum-js'); + + expect( + nonRumServices.every((item) => { + return ( + typeof item.transactionErrorRate?.value === 'number' && + item.transactionErrorRate.timeseries.length > 0 + ); + }) + ).to.be(true); + }); + } + ); + + registry.when( + 'APM Services overview with a trial license when data is loaded', + { config: 'trial', archives: [archiveName] }, + () => { + describe('with the default APM read user', () => { + describe('and fetching a list of services', () => { + let response: { + status: number; + body: APIReturnType<'GET /api/apm/services'>; + }; + + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + }); + + it('the response is successful', () => { + expect(response.status).to.eql(200); + }); + + it('there is at least one service', () => { + expect(response.body.items.length).to.be.greaterThan(0); + }); + + it('some items have a health status set', () => { + // Under the assumption that the loaded archive has + // at least one APM ML job, and the time range is longer + // than 15m, at least one items should have a health status + // set. Note that we currently have a bug where healthy + // services report as unknown (so without any health status): + // https://github.com/elastic/kibana/issues/77083 + + const healthStatuses = sortBy(response.body.items, 'serviceName').map( + (item: any) => item.healthStatus + ); + + expect(healthStatuses.filter(Boolean).length).to.be.greaterThan(0); + + expectSnapshot(healthStatuses).toMatchInline(` + Array [ + "healthy", + "healthy", + "healthy", + "healthy", + "healthy", + "healthy", + "healthy", + "healthy", + "healthy", + ] + `); + }); + }); + }); + + describe('with a user that does not have access to ML', () => { + let response: PromiseReturnType; + before(async () => { + response = await supertestAsApmReadUserWithoutMlAccess.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); + }); + + it('the response is successful', () => { + expect(response.status).to.eql(200); + }); + + it('there is at least one service', () => { + expect(response.body.items.length).to.be.greaterThan(0); + }); + + it('contains no health statuses', () => { + const definedHealthStatuses = response.body.items + .map((item: any) => item.healthStatus) + .filter(Boolean); + + expect(definedHealthStatuses.length).to.be(0); + }); + }); + + describe('and fetching a list of services with a filter', () => { + let response: PromiseReturnType; + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${encodeURIComponent( + `{"kuery":"service.name:opbeans-java","environment":"ENVIRONMENT_ALL"}` + )}` + ); + }); + + it('does not return health statuses for services that are not found in APM data', () => { + expect(response.status).to.be(200); + + expect(response.body.items.length).to.be(1); + + expect(response.body.items[0].serviceName).to.be('opbeans-java'); + }); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts similarity index 75% rename from x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts rename to x-pack/test/apm_api_integration/tests/services/transaction_types.ts index fcfe1660d58e2..c45f4083ef8da 100644 --- a/x-pack/test/apm_api_integration/basic/tests/services/transaction_types.ts +++ b/x-pack/test/apm_api_integration/tests/services/transaction_types.ts @@ -5,12 +5,12 @@ */ import expect from '@kbn/expect'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; 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]; @@ -19,8 +19,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { const start = encodeURIComponent(metadata.start); const end = encodeURIComponent(metadata.end); - describe('Transaction types', () => { - describe('when data is not loaded ', () => { + registry.when( + 'Transaction types when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles empty state', async () => { const response = await supertest.get( `/api/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` @@ -30,12 +32,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body.transactionTypes.length).to.be(0); }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Transaction types when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('handles empty state', async () => { const response = await supertest.get( `/api/apm/services/opbeans-node/transaction_types?start=${start}&end=${end}` @@ -53,6 +56,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { } `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts b/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts new file mode 100644 index 0000000000000..0b58dd5908c60 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/agent_configuration.ts @@ -0,0 +1,495 @@ +/* + * 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 { omit, orderBy } from 'lodash'; +import { AgentConfigurationIntake } from '../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigSearchParams } from '../../../../plugins/apm/server/routes/settings/agent_configuration'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function agentConfigurationTests({ getService }: FtrProviderContext) { + const supertestRead = getService('supertestAsApmReadUser'); + const supertestWrite = getService('supertestAsApmWriteUser'); + const log = getService('log'); + + const archiveName = 'apm_8.0.0'; + + function getServices() { + return supertestRead + .get(`/api/apm/settings/agent-configuration/services`) + .set('kbn-xsrf', 'foo'); + } + + function getEnvironments(serviceName: string) { + return supertestRead + .get(`/api/apm/settings/agent-configuration/environments?serviceName=${serviceName}`) + .set('kbn-xsrf', 'foo'); + } + + function getAgentName(serviceName: string) { + return supertestRead + .get(`/api/apm/settings/agent-configuration/agent_name?serviceName=${serviceName}`) + .set('kbn-xsrf', 'foo'); + } + + function searchConfigurations(configuration: AgentConfigSearchParams) { + return supertestRead + .post(`/api/apm/settings/agent-configuration/search`) + .send(configuration) + .set('kbn-xsrf', 'foo'); + } + + function getAllConfigurations() { + return supertestRead.get(`/api/apm/settings/agent-configuration`).set('kbn-xsrf', 'foo'); + } + + async function createConfiguration(config: AgentConfigurationIntake, { user = 'write' } = {}) { + log.debug('creating configuration', config.service); + const supertestClient = user === 'read' ? supertestRead : supertestWrite; + + const res = await supertestClient + .put(`/api/apm/settings/agent-configuration`) + .send(config) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + async function updateConfiguration(config: AgentConfigurationIntake, { user = 'write' } = {}) { + log.debug('updating configuration', config.service); + const supertestClient = user === 'read' ? supertestRead : supertestWrite; + + const res = await supertestClient + .put(`/api/apm/settings/agent-configuration?overwrite=true`) + .send(config) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + async function deleteConfiguration( + { service }: AgentConfigurationIntake, + { user = 'write' } = {} + ) { + log.debug('deleting configuration', service); + const supertestClient = user === 'read' ? supertestRead : supertestWrite; + + const res = await supertestClient + .delete(`/api/apm/settings/agent-configuration`) + .send({ service }) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + function throwOnError(res: any) { + const { statusCode, req, body } = res; + if (statusCode !== 200) { + const e = new Error(` + Endpoint: ${req.method} ${req.path} + Service: ${JSON.stringify(res.request._data.service)} + Status code: ${statusCode} + Response: ${body.message}`); + + // @ts-ignore + e.res = res; + + throw e; + } + } + + registry.when( + 'agent configuration when no data is loaded', + { config: 'basic', archives: [] }, + () => { + it('handles the empty state for services', async () => { + const { body } = await getServices(); + expect(body).to.eql(['ALL_OPTION_VALUE']); + }); + + it('handles the empty state for environments', async () => { + const { body } = await getEnvironments('myservice'); + expect(body).to.eql([{ name: 'ALL_OPTION_VALUE', alreadyConfigured: false }]); + }); + + it('handles the empty state for agent names', async () => { + const { body } = await getAgentName('myservice'); + expect(body).to.eql({}); + }); + + describe('as a read-only user', () => { + const newConfig = { service: {}, settings: { transaction_sample_rate: '0.55' } }; + it('throws when attempting to create config', async () => { + try { + await createConfiguration(newConfig, { user: 'read' }); + + // ensure that `createConfiguration` throws + expect(true).to.be(false); + } catch (e) { + expect(e.res.statusCode).to.be(403); + } + }); + + describe('when a configuration already exists', () => { + before(async () => createConfiguration(newConfig)); + after(async () => deleteConfiguration(newConfig)); + + it('throws when attempting to update config', async () => { + try { + await updateConfiguration(newConfig, { user: 'read' }); + + // ensure that `updateConfiguration` throws + expect(true).to.be(false); + } catch (e) { + expect(e.res.statusCode).to.be(403); + } + }); + + it('throws when attempting to delete config', async () => { + try { + await deleteConfiguration(newConfig, { user: 'read' }); + + // ensure that `deleteConfiguration` throws + expect(true).to.be(false); + } catch (e) { + expect(e.res.statusCode).to.be(403); + } + }); + }); + }); + + describe('when creating one configuration', () => { + const newConfig = { + service: {}, + settings: { transaction_sample_rate: '0.55' }, + }; + + const searchParams = { + service: { name: 'myservice', environment: 'development' }, + etag: '7312bdcc34999629a3d39df24ed9b2a7553c0c39', + }; + + it('can create and delete config', async () => { + // assert that config does not exist + const res1 = await searchConfigurations(searchParams); + expect(res1.status).to.equal(404); + + // assert that config was created + await createConfiguration(newConfig); + const res2 = await searchConfigurations(searchParams); + expect(res2.status).to.equal(200); + + // assert that config was deleted + await deleteConfiguration(newConfig); + const res3 = await searchConfigurations(searchParams); + expect(res3.status).to.equal(404); + }); + + describe('when a configuration exists', () => { + before(async () => createConfiguration(newConfig)); + after(async () => deleteConfiguration(newConfig)); + + it('can find the config', async () => { + const { status, body } = await searchConfigurations(searchParams); + expect(status).to.equal(200); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: '0.55' }); + }); + + it('can list the config', async () => { + const { status, body } = await getAllConfigurations(); + expect(status).to.equal(200); + expect(omitTimestamp(body)).to.eql([ + { + service: {}, + settings: { transaction_sample_rate: '0.55' }, + applied_by_agent: false, + etag: 'eb88a8997666cc4b33745ef355a1bbd7c4782f2d', + }, + ]); + }); + + it('can update the config', async () => { + await updateConfiguration({ + service: {}, + settings: { transaction_sample_rate: '0.85' }, + }); + const { status, body } = await searchConfigurations(searchParams); + expect(status).to.equal(200); + expect(body._source.service).to.eql({}); + expect(body._source.settings).to.eql({ transaction_sample_rate: '0.85' }); + }); + }); + }); + + describe('when creating multiple configurations', () => { + const configs = [ + { + service: {}, + settings: { transaction_sample_rate: '0.1' }, + }, + { + service: { name: 'my_service' }, + settings: { transaction_sample_rate: '0.2' }, + }, + { + service: { name: 'my_service', environment: 'development' }, + settings: { transaction_sample_rate: '0.3' }, + }, + { + service: { environment: 'production' }, + settings: { transaction_sample_rate: '0.4' }, + }, + { + service: { environment: 'development' }, + settings: { transaction_sample_rate: '0.5' }, + }, + ]; + + before(async () => { + await Promise.all(configs.map((config) => createConfiguration(config))); + }); + + after(async () => { + await Promise.all(configs.map((config) => deleteConfiguration(config))); + }); + + const agentsRequests = [ + { + service: { name: 'non_existing_service', environment: 'non_existing_env' }, + expectedSettings: { transaction_sample_rate: '0.1' }, + }, + { + service: { name: 'my_service', environment: 'non_existing_env' }, + expectedSettings: { transaction_sample_rate: '0.2' }, + }, + { + service: { name: 'my_service', environment: 'production' }, + expectedSettings: { transaction_sample_rate: '0.2' }, + }, + { + service: { name: 'my_service', environment: 'development' }, + expectedSettings: { transaction_sample_rate: '0.3' }, + }, + { + service: { name: 'non_existing_service', environment: 'production' }, + expectedSettings: { transaction_sample_rate: '0.4' }, + }, + { + service: { name: 'non_existing_service', environment: 'development' }, + expectedSettings: { transaction_sample_rate: '0.5' }, + }, + ]; + + it('can list all configs', async () => { + const { status, body } = await getAllConfigurations(); + expect(status).to.equal(200); + expect(orderBy(omitTimestamp(body), ['settings.transaction_sample_rate'])).to.eql([ + { + service: {}, + settings: { transaction_sample_rate: '0.1' }, + applied_by_agent: false, + etag: '0758cb18817de60cca29e07480d472694239c4c3', + }, + { + service: { name: 'my_service' }, + settings: { transaction_sample_rate: '0.2' }, + applied_by_agent: false, + etag: 'e04737637056fdf1763bf0ef0d3fcb86e89ae5fc', + }, + { + service: { name: 'my_service', environment: 'development' }, + settings: { transaction_sample_rate: '0.3' }, + applied_by_agent: false, + etag: 'af4dac62621b6762e6281481d1f7523af1124120', + }, + { + service: { environment: 'production' }, + settings: { transaction_sample_rate: '0.4' }, + applied_by_agent: false, + etag: '8d1bf8e6b778b60af351117e2cf53fb1ee570068', + }, + { + service: { environment: 'development' }, + settings: { transaction_sample_rate: '0.5' }, + applied_by_agent: false, + etag: '4ce40da57e3c71daca704121c784b911ec05ae81', + }, + ]); + }); + + for (const agentRequest of agentsRequests) { + it(`${agentRequest.service.name} / ${agentRequest.service.environment}`, async () => { + const { status, body } = await searchConfigurations({ + service: agentRequest.service, + etag: 'abc', + }); + + expect(status).to.equal(200); + expect(body._source.settings).to.eql(agentRequest.expectedSettings); + }); + } + }); + + describe('when an agent retrieves a configuration', () => { + const config = { + service: { name: 'myservice', environment: 'development' }, + settings: { transaction_sample_rate: '0.9' }, + }; + const configProduction = { + service: { name: 'myservice', environment: 'production' }, + settings: { transaction_sample_rate: '0.9' }, + }; + let etag: string; + + before(async () => { + log.debug('creating agent configuration'); + await createConfiguration(config); + await createConfiguration(configProduction); + }); + + after(async () => { + await deleteConfiguration(config); + await deleteConfiguration(configProduction); + }); + + it(`should have 'applied_by_agent=false' before supplying etag`, async () => { + const res1 = await searchConfigurations({ + service: { name: 'myservice', environment: 'development' }, + }); + + etag = res1.body._source.etag; + + const res2 = await searchConfigurations({ + service: { name: 'myservice', environment: 'development' }, + etag, + }); + + expect(res1.body._source.applied_by_agent).to.be(false); + expect(res2.body._source.applied_by_agent).to.be(false); + }); + + it(`should have 'applied_by_agent=true' after supplying etag`, async () => { + await searchConfigurations({ + service: { name: 'myservice', environment: 'development' }, + etag, + }); + + async function hasBeenAppliedByAgent() { + const { body } = await searchConfigurations({ + service: { name: 'myservice', environment: 'development' }, + }); + + return body._source.applied_by_agent; + } + + // wait until `applied_by_agent` has been updated in elasticsearch + expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); + }); + it(`should have 'applied_by_agent=false' before marking as applied`, async () => { + const res1 = await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + }); + + expect(res1.body._source.applied_by_agent).to.be(false); + }); + it(`should have 'applied_by_agent=true' when 'mark_as_applied_by_agent' attribute is true`, async () => { + await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + mark_as_applied_by_agent: true, + }); + + async function hasBeenAppliedByAgent() { + const { body } = await searchConfigurations({ + service: { name: 'myservice', environment: 'production' }, + }); + + return body._source.applied_by_agent; + } + + // wait until `applied_by_agent` has been updated in elasticsearch + expect(await waitFor(hasBeenAppliedByAgent)).to.be(true); + }); + }); + } + ); + + registry.when( + 'agent configuration when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { + it('returns all services', async () => { + const { body } = await getServices(); + expectSnapshot(body).toMatchInline(` + Array [ + "ALL_OPTION_VALUE", + "kibana", + "kibana-frontend", + "opbeans-dotnet", + "opbeans-go", + "opbeans-java", + "opbeans-node", + "opbeans-python", + "opbeans-ruby", + "opbeans-rum", + ] + `); + }); + + it('returns the environments, all unconfigured', async () => { + const { body } = await getEnvironments('opbeans-node'); + + expect(body.map((item: { name: string }) => item.name)).to.contain('ALL_OPTION_VALUE'); + + expect( + body.every((item: { alreadyConfigured: boolean }) => item.alreadyConfigured === false) + ).to.be(true); + + expectSnapshot(body).toMatchInline(` + Array [ + Object { + "alreadyConfigured": false, + "name": "ALL_OPTION_VALUE", + }, + Object { + "alreadyConfigured": false, + "name": "testing", + }, + ] + `); + }); + + it('returns the agent names', async () => { + const { body } = await getAgentName('opbeans-node'); + expect(body).to.eql({ agentName: 'nodejs' }); + }); + } + ); +} + +async function waitFor(cb: () => Promise, retries = 50): Promise { + if (retries === 0) { + throw new Error(`Maximum number of retries reached`); + } + + const res = await cb(); + if (!res) { + await new Promise((resolve) => setTimeout(resolve, 100)); + return waitFor(cb, retries - 1); + } + return res; +} + +function omitTimestamp(configs: AgentConfigurationIntake[]) { + return configs.map((config: AgentConfigurationIntake) => omit(config, '@timestamp')); +} diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts new file mode 100644 index 0000000000000..269375ef080de --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/basic.ts @@ -0,0 +1,53 @@ +/* + * 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 { registry } from '../../../common/registry'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function apiTest({ getService }: FtrProviderContext) { + const noAccessUser = getService('supertestAsNoAccessUser'); + const readUser = getService('supertestAsApmReadUser'); + const writeUser = getService('supertestAsApmWriteUser'); + + type SupertestAsUser = typeof noAccessUser | typeof readUser | typeof writeUser; + + function getJobs(user: SupertestAsUser) { + return user.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + } + + function createJobs(user: SupertestAsUser, environments: string[]) { + return user + .post(`/api/apm/settings/anomaly-detection/jobs`) + .send({ environments }) + .set('kbn-xsrf', 'foo'); + } + + async function expectForbidden(user: SupertestAsUser) { + const { body: getJobsBody } = await getJobs(user); + expect(getJobsBody.statusCode).to.be(403); + expect(getJobsBody.error).to.be('Forbidden'); + + const { body: createJobsBody } = await createJobs(user, ['production', 'staging']); + + expect(createJobsBody.statusCode).to.be(403); + expect(getJobsBody.error).to.be('Forbidden'); + } + + registry.when('ML jobs return a 403 for', { config: 'basic', archives: [] }, () => { + it('user without access', async () => { + await expectForbidden(noAccessUser); + }); + + it('read user', async () => { + await expectForbidden(readUser); + }); + + it('write user', async () => { + await expectForbidden(writeUser); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts new file mode 100644 index 0000000000000..4878d5031f040 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/no_access_user.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { registry } from '../../../common/registry'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function apiTest({ getService }: FtrProviderContext) { + const noAccessUser = getService('supertestAsNoAccessUser'); + + function getJobs() { + return noAccessUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + } + + function createJobs(environments: string[]) { + return noAccessUser + .post(`/api/apm/settings/anomaly-detection/jobs`) + .send({ environments }) + .set('kbn-xsrf', 'foo'); + } + + registry.when('ML jobs', { config: 'trial', archives: [] }, () => { + describe('when user does not have read access to ML', () => { + describe('when calling the endpoint for listing jobs', () => { + it('returns an error because the user does not have access', async () => { + const { body } = await getJobs(); + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + }); + }); + + describe('when calling create endpoint', () => { + it('returns an error because the user does not have access', async () => { + const { body } = await createJobs(['production', 'staging']); + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts new file mode 100644 index 0000000000000..a5fabe66af6f5 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/read_user.ts @@ -0,0 +1,46 @@ +/* + * 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 { registry } from '../../../common/registry'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function apiTest({ getService }: FtrProviderContext) { + const apmReadUser = getService('supertestAsApmReadUser'); + + function getJobs() { + return apmReadUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + } + + function createJobs(environments: string[]) { + return apmReadUser + .post(`/api/apm/settings/anomaly-detection/jobs`) + .send({ environments }) + .set('kbn-xsrf', 'foo'); + } + + registry.when('ML jobs', { config: 'trial', archives: [] }, () => { + describe('when user has read access to ML', () => { + describe('when calling the endpoint for listing jobs', () => { + it('returns a list of jobs', async () => { + const { body } = await getJobs(); + + expect(body.jobs.length).to.be(0); + expect(body.hasLegacyJobs).to.be(false); + }); + }); + + describe('when calling create endpoint', () => { + it('returns an error because the user does not have access', async () => { + const { body } = await createJobs(['production', 'staging']); + + expect(body.statusCode).to.be(403); + expect(body.error).to.be('Forbidden'); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts new file mode 100644 index 0000000000000..5260d234eb9c7 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/anomaly_detection/write_user.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { registry } from '../../../common/registry'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +export default function apiTest({ getService }: FtrProviderContext) { + const apmWriteUser = getService('supertestAsApmWriteUser'); + + function getJobs() { + return apmWriteUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); + } + + function createJobs(environments: string[]) { + return apmWriteUser + .post(`/api/apm/settings/anomaly-detection/jobs`) + .send({ environments }) + .set('kbn-xsrf', 'foo'); + } + + function deleteJobs(jobIds: string[]) { + return apmWriteUser.post(`/api/ml/jobs/delete_jobs`).send({ jobIds }).set('kbn-xsrf', 'foo'); + } + + registry.when('ML jobs', { config: 'trial', archives: [] }, () => { + describe('when user has write access to ML', () => { + after(async () => { + const res = await getJobs(); + const jobIds = res.body.jobs.map((job: any) => job.job_id); + await deleteJobs(jobIds); + }); + + describe('when calling the endpoint for listing jobs', () => { + it('returns a list of jobs', async () => { + const { body } = await getJobs(); + expect(body.jobs.length).to.be(0); + expect(body.hasLegacyJobs).to.be(false); + }); + }); + + describe('when calling create endpoint', () => { + it('creates two jobs', async () => { + await createJobs(['production', 'staging']); + + const { body } = await getJobs(); + expect(body.hasLegacyJobs).to.be(false); + expect(body.jobs.map((job: any) => job.environment)).to.eql(['production', 'staging']); + }); + }); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts new file mode 100644 index 0000000000000..3df905eabeace --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/settings/custom_link.ts @@ -0,0 +1,183 @@ +/* + * 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 URL from 'url'; +import expect from '@kbn/expect'; +import { CustomLink } from '../../../../plugins/apm/common/custom_link/custom_link_types'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function customLinksTests({ getService }: FtrProviderContext) { + const supertestRead = getService('supertest'); + const supertestWrite = getService('supertestAsApmWriteUser'); + const log = getService('log'); + + const archiveName = 'apm_8.0.0'; + + registry.when('Custom links with a basic license', { config: 'basic', archives: [] }, () => { + it('returns a 403 forbidden', async () => { + const customLink = { + url: 'https://elastic.co', + label: 'with filters', + filters: [ + { key: 'service.name', value: 'baz' }, + { key: 'transaction.type', value: 'qux' }, + ], + } as CustomLink; + const response = await supertestWrite + .post(`/api/apm/settings/custom_links`) + .send(customLink) + .set('kbn-xsrf', 'foo'); + + expect(response.status).to.be(403); + + expectSnapshot(response.body.message).toMatchInline( + `"To create custom links, you must be subscribed to an Elastic Gold license or above. With it, you'll have the ability to create custom links to improve your workflow when analyzing your services."` + ); + }); + }); + + registry.when( + 'Custom links with a trial license and data', + { config: 'trial', archives: [archiveName] }, + () => { + before(async () => { + const customLink = { + url: 'https://elastic.co', + label: 'with filters', + filters: [ + { key: 'service.name', value: 'baz' }, + { key: 'transaction.type', value: 'qux' }, + ], + } as CustomLink; + await createCustomLink(customLink); + }); + it('fetches a custom link', async () => { + const { status, body } = await searchCustomLinks({ + 'service.name': 'baz', + 'transaction.type': 'qux', + }); + const { label, url, filters } = body[0]; + + expect(status).to.equal(200); + expect({ label, url, filters }).to.eql({ + label: 'with filters', + url: 'https://elastic.co', + filters: [ + { key: 'service.name', value: 'baz' }, + { key: 'transaction.type', value: 'qux' }, + ], + }); + }); + it('updates a custom link', async () => { + let { status, body } = await searchCustomLinks({ + 'service.name': 'baz', + 'transaction.type': 'qux', + }); + expect(status).to.equal(200); + await updateCustomLink(body[0].id, { + label: 'foo', + url: 'https://elastic.co?service.name={{service.name}}', + filters: [ + { key: 'service.name', value: 'quz' }, + { key: 'transaction.name', value: 'bar' }, + ], + }); + ({ status, body } = await searchCustomLinks({ + 'service.name': 'quz', + 'transaction.name': 'bar', + })); + const { label, url, filters } = body[0]; + expect(status).to.equal(200); + expect({ label, url, filters }).to.eql({ + label: 'foo', + url: 'https://elastic.co?service.name={{service.name}}', + filters: [ + { key: 'service.name', value: 'quz' }, + { key: 'transaction.name', value: 'bar' }, + ], + }); + }); + it('deletes a custom link', async () => { + let { status, body } = await searchCustomLinks({ + 'service.name': 'quz', + 'transaction.name': 'bar', + }); + expect(status).to.equal(200); + await deleteCustomLink(body[0].id); + ({ status, body } = await searchCustomLinks({ + 'service.name': 'quz', + 'transaction.name': 'bar', + })); + expect(status).to.equal(200); + expect(body).to.eql([]); + }); + + describe('transaction', () => { + it('fetches a transaction sample', async () => { + const response = await supertestRead.get( + '/api/apm/settings/custom_links/transaction?service.name=opbeans-java' + ); + expect(response.status).to.be(200); + expect(response.body.service.name).to.eql('opbeans-java'); + }); + }); + } + ); + + function searchCustomLinks(filters?: any) { + const path = URL.format({ + pathname: `/api/apm/settings/custom_links`, + query: filters, + }); + return supertestRead.get(path).set('kbn-xsrf', 'foo'); + } + + async function createCustomLink(customLink: CustomLink) { + log.debug('creating configuration', customLink); + const res = await supertestWrite + .post(`/api/apm/settings/custom_links`) + .send(customLink) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + async function updateCustomLink(id: string, customLink: CustomLink) { + log.debug('updating configuration', id, customLink); + const res = await supertestWrite + .put(`/api/apm/settings/custom_links/${id}`) + .send(customLink) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + async function deleteCustomLink(id: string) { + log.debug('deleting configuration', id); + const res = await supertestWrite + .delete(`/api/apm/settings/custom_links/${id}`) + .set('kbn-xsrf', 'foo'); + + throwOnError(res); + + return res; + } + + function throwOnError(res: any) { + const { statusCode, req, body } = res; + if (statusCode !== 200) { + throw new Error(` + Endpoint: ${req.method} ${req.path} + Service: ${JSON.stringify(res.request._data.service)} + Status code: ${statusCode} + Response: ${body.message}`); + } + } +} diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap b/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap similarity index 99% rename from x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap rename to x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap index 56e82d752dccd..c8104b9858027 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/__snapshots__/top_traces.snap +++ b/x-pack/test/apm_api_integration/tests/traces/__snapshots__/top_traces.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (basic) Traces Top traces when data is loaded returns the correct buckets 1`] = ` +exports[`APM API tests basic apm_8.0.0 Top traces when data is loaded returns the correct buckets 1`] = ` Array [ Object { "averageResponseTime": 1733, diff --git a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts similarity index 81% rename from x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts rename to x-pack/test/apm_api_integration/tests/traces/top_traces.ts index 2ce3ba3838292..4754b3faa20ad 100644 --- a/x-pack/test/apm_api_integration/basic/tests/traces/top_traces.ts +++ b/x-pack/test/apm_api_integration/tests/traces/top_traces.ts @@ -5,12 +5,12 @@ */ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; 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]; @@ -20,28 +20,28 @@ export default function ApiTest({ getService }: FtrProviderContext) { const end = encodeURIComponent(metadata.end); const uiFilters = encodeURIComponent(JSON.stringify({})); - describe('Top traces', () => { - describe('when data is not loaded ', () => { - it('handles empty state', async () => { - const response = await supertest.get( - `/api/apm/traces?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); + registry.when('Top traces when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles empty state', async () => { + const response = await supertest.get( + `/api/apm/traces?start=${start}&end=${end}&uiFilters=${uiFilters}` + ); - expect(response.status).to.be(200); - expect(response.body.items.length).to.be(0); - expect(response.body.isAggregationAccurate).to.be(true); - }); + expect(response.status).to.be(200); + expect(response.body.items.length).to.be(0); + expect(response.body.isAggregationAccurate).to.be(true); }); + }); - describe('when data is loaded', () => { + registry.when( + 'Top traces when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { let response: any; before(async () => { - await esArchiver.load(archiveName); response = await supertest.get( `/api/apm/traces?start=${start}&end=${end}&uiFilters=${uiFilters}` ); }); - after(() => esArchiver.unload(archiveName)); it('returns the correct status code', async () => { expect(response.status).to.be(200); @@ -116,6 +116,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { ] `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/breakdown.snap similarity index 98% rename from x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap rename to x-pack/test/apm_api_integration/tests/transactions/__snapshots__/breakdown.snap index 25aa68d2a86b1..cfdd27b594956 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/breakdown.snap +++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/breakdown.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (basic) Transactions Breakdown when data is loaded returns the transaction breakdown for a service 1`] = ` +exports[`APM API tests basic apm_8.0.0 when data is loaded returns the transaction breakdown for a service 1`] = ` Object { "timeseries": Array [ Object { @@ -1019,7 +1019,7 @@ Object { } `; -exports[`APM specs (basic) Transactions Breakdown when data is loaded returns the transaction breakdown for a transaction group 9`] = ` +exports[`APM API tests basic apm_8.0.0 when data is loaded returns the transaction breakdown for a transaction group 9`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap similarity index 95% rename from x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap rename to x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap index 3b67a86ba84e8..d97d39cda1b8d 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/error_rate.snap +++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/error_rate.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (basic) Transactions Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = ` +exports[`APM API tests basic apm_8.0.0 Error rate when data is loaded returns the transaction error rate has the correct error rate 1`] = ` Array [ Object { "x": 1607435850000, diff --git a/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap new file mode 100644 index 0000000000000..a384ca2c9364e --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/latency.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded when not defined environments seleted should return the correct anomaly boundaries 1`] = ` +Array [ + Object { + "x": 1607436000000, + "y": 0, + "y0": 0, + }, + Object { + "x": 1607436900000, + "y": 0, + "y0": 0, + }, +] +`; + +exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` +Array [ + Object { + "x": 1607436000000, + "y": 1625128.56211579, + "y0": 7533.02707532227, + }, + Object { + "x": 1607436900000, + "y": 1660982.24115757, + "y0": 5732.00699123528, + }, +] +`; + +exports[`APM API tests trial apm_8.0.0 Transaction latency with a trial license when data is loaded with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` +Array [ + Object { + "x": 1607436000000, + "y": 1625128.56211579, + "y0": 7533.02707532227, + }, + Object { + "x": 1607436900000, + "y": 1660982.24115757, + "y0": 5732.00699123528, + }, +] +`; diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/top_transaction_groups.snap similarity index 96% rename from x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap rename to x-pack/test/apm_api_integration/tests/transactions/__snapshots__/top_transaction_groups.snap index 473305f3e39af..67a02e416de51 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/top_transaction_groups.snap +++ b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/top_transaction_groups.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`APM specs (basic) Transactions Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` +exports[`APM API tests basic apm_8.0.0 Top transaction groups when data is loaded returns the correct buckets (when ignoring samples) 1`] = ` Array [ Object { "averageResponseTime": 2722.75, diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/transaction_charts.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/transaction_charts.snap similarity index 100% rename from x-pack/test/apm_api_integration/basic/tests/transactions/__snapshots__/transaction_charts.snap rename to x-pack/test/apm_api_integration/tests/transactions/__snapshots__/transaction_charts.snap diff --git a/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/transactions_charts.snap b/x-pack/test/apm_api_integration/tests/transactions/__snapshots__/transactions_charts.snap similarity index 100% rename from x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/transactions_charts.snap rename to x-pack/test/apm_api_integration/tests/transactions/__snapshots__/transactions_charts.snap diff --git a/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts new file mode 100644 index 0000000000000..4a9dcf9273003 --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/transactions/breakdown.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const archiveName = 'apm_8.0.0'; + const metadata = archives_metadata[archiveName]; + + const start = encodeURIComponent(metadata.start); + const end = encodeURIComponent(metadata.end); + const transactionType = 'request'; + const transactionName = 'GET /api'; + const uiFilters = encodeURIComponent(JSON.stringify({})); + + registry.when('Breakdown when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + ); + expect(response.status).to.be(200); + expect(response.body).to.eql({ timeseries: [] }); + }); + }); + + registry.when('when data is loaded', { config: 'basic', archives: [archiveName] }, () => { + it('returns the transaction breakdown for a service', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body).toMatch(); + }); + it('returns the transaction breakdown for a transaction group', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}&transactionName=${transactionName}` + ); + + expect(response.status).to.be(200); + + const { timeseries } = response.body; + + const numberOfSeries = timeseries.length; + + expectSnapshot(numberOfSeries).toMatchInline(`1`); + + const { title, color, type, data, hideLegend, legendValue } = timeseries[0]; + + const nonNullDataPoints = data.filter((y: number | null) => y !== null); + + expectSnapshot(nonNullDataPoints.length).toMatchInline(`61`); + + expectSnapshot( + data.slice(0, 5).map(({ x, y }: { x: number; y: number | null }) => { + return { + x: new Date(x ?? NaN).toISOString(), + y, + }; + }) + ).toMatchInline(` + Array [ + Object { + "x": "2020-12-08T13:57:30.000Z", + "y": null, + }, + Object { + "x": "2020-12-08T13:58:00.000Z", + "y": null, + }, + Object { + "x": "2020-12-08T13:58:30.000Z", + "y": 1, + }, + Object { + "x": "2020-12-08T13:59:00.000Z", + "y": 1, + }, + Object { + "x": "2020-12-08T13:59:30.000Z", + "y": null, + }, + ] + `); + + expectSnapshot(title).toMatchInline(`"app"`); + expectSnapshot(color).toMatchInline(`"#54b399"`); + expectSnapshot(type).toMatchInline(`"areaStacked"`); + expectSnapshot(hideLegend).toMatchInline(`false`); + expectSnapshot(legendValue).toMatchInline(`"100%"`); + + expectSnapshot(data).toMatch(); + }); + it('returns the transaction breakdown sorted by name', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transaction/charts/breakdown?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` + ); + + expect(response.status).to.be(200); + expectSnapshot(response.body.timeseries.map((serie: { title: string }) => serie.title)) + .toMatchInline(` + Array [ + "app", + "http", + "postgresql", + "redis", + ] + `); + }); + }); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/distribution.ts b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts similarity index 85% rename from x-pack/test/apm_api_integration/basic/tests/transactions/distribution.ts rename to x-pack/test/apm_api_integration/tests/transactions/distribution.ts index 890b6af728d5a..5d918b479111f 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/distribution.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/distribution.ts @@ -6,12 +6,12 @@ import expect from '@kbn/expect'; import qs from 'querystring'; import { isEmpty } from 'lodash'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; 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]; @@ -24,8 +24,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { transactionType: 'request', })}`; - describe('Transaction groups distribution', () => { - describe('when data is not loaded ', () => { + registry.when( + 'Transaction groups distribution when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles empty state', async () => { const response = await supertest.get(url); @@ -34,15 +36,17 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body.noHits).to.be(true); expect(response.body.buckets.length).to.be(0); }); - }); + } + ); - describe('when data is loaded', () => { + registry.when( + 'Transaction groups distribution when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { let response: any; before(async () => { - await esArchiver.load(archiveName); response = await supertest.get(url); }); - after(() => esArchiver.unload(archiveName)); it('returns the correct metadata', () => { expect(response.status).to.be(200); @@ -91,6 +95,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { ] `); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/error_rate.ts b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts similarity index 71% rename from x-pack/test/apm_api_integration/basic/tests/transactions/error_rate.ts rename to x-pack/test/apm_api_integration/tests/transactions/error_rate.ts index 22d9a7eba7fbf..487f2efbae400 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/error_rate.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/error_rate.ts @@ -6,12 +6,12 @@ import expect from '@kbn/expect'; import { first, last } from 'lodash'; import { format } from 'url'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; @@ -20,28 +20,27 @@ export default function ApiTest({ getService }: FtrProviderContext) { const uiFilters = '{}'; const transactionType = 'request'; - describe('Error rate', () => { - const url = format({ - pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', - query: { start, end, uiFilters, transactionType }, - }); + const url = format({ + pathname: '/api/apm/services/opbeans-java/transactions/charts/error_rate', + query: { start, end, uiFilters, transactionType }, + }); - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get(url); - expect(response.status).to.be(200); + registry.when('Error rate when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get(url); + expect(response.status).to.be(200); - expect(response.body.noHits).to.be(true); + expect(response.body.noHits).to.be(true); - expect(response.body.transactionErrorRate.length).to.be(0); - expect(response.body.average).to.be(null); - }); + expect(response.body.transactionErrorRate.length).to.be(0); + expect(response.body.average).to.be(null); }); + }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - + registry.when( + 'Error rate when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { describe('returns the transaction error rate', () => { let errorRateResponse: { transactionErrorRate: Array<{ x: number; y: number | null }>; @@ -89,6 +88,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expectSnapshot(errorRateResponse.transactionErrorRate).toMatch(); }); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/tests/transactions/latency.ts new file mode 100644 index 0000000000000..c860b0e75495c --- /dev/null +++ b/x-pack/test/apm_api_integration/tests/transactions/latency.ts @@ -0,0 +1,243 @@ +/* + * 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 { PromiseReturnType } from '../../../../plugins/observability/typings/common'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const archiveName = 'apm_8.0.0'; + + const range = archives_metadata[archiveName]; + + // url parameters + const start = encodeURIComponent(range.start); + const end = encodeURIComponent(range.end); + + registry.when( + 'Latency with a basic license when data is not loaded ', + { config: 'basic', archives: [] }, + () => { + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' })); + it('returns 400 when latencyAggregationType is not informed', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request` + ); + + expect(response.status).to.be(400); + }); + + it('returns 400 when transactionType is not informed', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + + expect(response.status).to.be(400); + }); + + it('handles the empty state', async () => { + const response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&latencyAggregationType=avg&transactionType=request` + ); + + expect(response.status).to.be(200); + + expect(response.body.overallAvgDuration).to.be(null); + expect(response.body.latencyTimeseries.length).to.be(0); + }); + } + ); + + registry.when( + 'Latency with a basic license when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { + let response: PromiseReturnType; + + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' })); + + describe('average latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=avg` + ); + }); + + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); + }); + + describe('95th percentile latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p95` + ); + }); + + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); + }); + + describe('99th percentile latency type', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-node/transactions/charts/latency?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=request&latencyAggregationType=p99` + ); + }); + + it('returns average duration and timeseries', async () => { + expect(response.status).to.be(200); + expect(response.body.overallAvgDuration).not.to.be(null); + expect(response.body.latencyTimeseries.length).to.be.eql(61); + }); + }); + } + ); + + registry.when( + 'Transaction latency with a trial license when data is loaded', + { config: 'trial', archives: [archiveName] }, + () => { + let response: PromiseReturnType; + + const transactionType = 'request'; + + describe('without environment', () => { + const uiFilters = encodeURIComponent(JSON.stringify({})); + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + }); + it('should return an error response', () => { + expect(response.status).to.eql(400); + }); + }); + + describe('without uiFilters', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` + ); + }); + it('should return an error response', () => { + expect(response.status).to.eql(400); + }); + }); + + describe('with environment selected in uiFilters', () => { + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' })); + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies of the selected environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-production-1369-high_mean_transaction_duration"` + ); + }); + + it('should return a non-empty anomaly series', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + + describe('when not defined environments seleted', () => { + const uiFilters = encodeURIComponent( + JSON.stringify({ environment: 'ENVIRONMENT_NOT_DEFINED' }) + ); + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-python/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies with no defined environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-environment_not_defined-5626-high_mean_transaction_duration"` + ); + }); + + it('should return the correct anomaly boundaries', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + + describe('with all environments selected', () => { + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' })); + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should not return anomaly timeseries data', () => { + expect(response.body).to.not.have.property('anomalyTimeseries'); + }); + }); + + describe('with environment selected and empty kuery filter', () => { + const uiFilters = encodeURIComponent( + JSON.stringify({ kuery: '', environment: 'production' }) + ); + before(async () => { + response = await supertest.get( + `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies of the selected environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-production-1369-high_mean_transaction_duration"` + ); + }); + + it('should return a non-empty anomaly series', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + } + ); +} diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/throughput.ts b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts similarity index 54% rename from x-pack/test/apm_api_integration/basic/tests/transactions/throughput.ts rename to x-pack/test/apm_api_integration/tests/transactions/throughput.ts index 1013f3a19d71f..475bef4e9b549 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/throughput.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/throughput.ts @@ -5,13 +5,13 @@ */ import expect from '@kbn/expect'; import url from 'url'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { PromiseReturnType } from '../../../../plugins/observability/typings/common'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; 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]; @@ -20,31 +20,30 @@ export default function ApiTest({ getService }: FtrProviderContext) { const { start, end } = metadata; const uiFilters = JSON.stringify({ environment: 'testing' }); - describe('Throughput', () => { - describe('when data is not loaded ', () => { - it('handles the empty state', async () => { - const response = await supertest.get( - url.format({ - pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`, - query: { - start, - end, - uiFilters, - transactionType: 'request', - }, - }) - ); + registry.when('Throughput when data is not loaded', { config: 'basic', archives: [] }, () => { + it('handles the empty state', async () => { + const response = await supertest.get( + url.format({ + pathname: `/api/apm/services/opbeans-node/transactions/charts/throughput`, + query: { + start, + end, + uiFilters, + transactionType: 'request', + }, + }) + ); - expect(response.status).to.be(200); + expect(response.status).to.be(200); - expect(response.body.throughputTimeseries.length).to.be(0); - }); + expect(response.body.throughputTimeseries.length).to.be(0); }); + }); - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - + registry.when( + 'Throughput when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { let response: PromiseReturnType; before(async () => { @@ -66,6 +65,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body.throughputTimeseries.length).to.be.greaterThan(0); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/top_transaction_groups.ts b/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts similarity index 80% rename from x-pack/test/apm_api_integration/basic/tests/transactions/top_transaction_groups.ts rename to x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts index dac36ae8b3303..70afb2ee384c4 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/top_transaction_groups.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/top_transaction_groups.ts @@ -5,8 +5,9 @@ */ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; +import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { registry } from '../../common/registry'; function sortTransactionGroups(items: any[]) { return sortBy(items, 'impact'); @@ -14,7 +15,6 @@ function sortTransactionGroups(items: any[]) { 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]; @@ -25,8 +25,10 @@ export default function ApiTest({ getService }: FtrProviderContext) { const uiFilters = encodeURIComponent(JSON.stringify({})); const transactionType = 'request'; - describe('Top transaction groups', () => { - describe('when data is not loaded ', () => { + registry.when( + 'Top transaction groups when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles empty state', async () => { const response = await supertest.get( `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` @@ -37,17 +39,19 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(response.body.isAggregationAccurate).to.be(true); expect(response.body.items.length).to.be(0); }); - }); + } + ); - describe('when data is loaded', () => { + registry.when( + 'Top transaction groups when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { let response: any; before(async () => { - await esArchiver.load(archiveName); response = await supertest.get( `/api/apm/services/opbeans-node/transactions/groups?start=${start}&end=${end}&uiFilters=${uiFilters}&transactionType=${transactionType}` ); }); - after(() => esArchiver.unload(archiveName)); it('returns the correct metadata', () => { expect(response.status).to.be(200); @@ -62,6 +66,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { it('returns the correct buckets (when ignoring samples)', async () => { expectSnapshot(sortTransactionGroups(response.body.items)).toMatch(); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_overview.ts similarity index 94% rename from x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts rename to x-pack/test/apm_api_integration/tests/transactions/transactions_groups_overview.ts index be978b2a82618..8818aaccdec01 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transactions/transactions_groups_overview.ts +++ b/x-pack/test/apm_api_integration/tests/transactions/transactions_groups_overview.ts @@ -7,18 +7,20 @@ import expect from '@kbn/expect'; import { pick, uniqBy, sortBy } from 'lodash'; import url from 'url'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; -import archives from '../../../common/fixtures/es_archiver/archives_metadata'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import archives from '../../common/fixtures/es_archiver/archives_metadata'; +import { registry } from '../../common/registry'; export default function ApiTest({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); const archiveName = 'apm_8.0.0'; const { start, end } = archives[archiveName]; - describe('Transactions groups overview', () => { - describe('when data is not loaded', () => { + registry.when( + 'Transaction groups overview when data is not loaded', + { config: 'basic', archives: [] }, + () => { it('handles the empty state', async () => { const response = await supertest.get( url.format({ @@ -45,12 +47,13 @@ export default function ApiTest({ getService }: FtrProviderContext) { isAggregationAccurate: true, }); }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); + } + ); + registry.when( + 'Top transaction groups when data is loaded', + { config: 'basic', archives: [archiveName] }, + () => { it('returns the correct data', async () => { const response = await supertest.get( url.format({ @@ -264,6 +267,6 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(uniqBy(items, 'name').length).to.eql(totalItems); }); - }); - }); + } + ); } diff --git a/x-pack/test/apm_api_integration/trial/config.ts b/x-pack/test/apm_api_integration/trial/config.ts index 94a6f808603c1..ba9ef4ca42597 100644 --- a/x-pack/test/apm_api_integration/trial/config.ts +++ b/x-pack/test/apm_api_integration/trial/config.ts @@ -4,10 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createTestConfig } from '../common/config'; +import { configs } from '../configs'; -export default createTestConfig({ - license: 'trial', - name: 'X-Pack APM API integration tests (trial)', - testFiles: [require.resolve('./tests')], -}); +export default configs.trial; diff --git a/x-pack/test/apm_api_integration/trial/tests/correlations/slow_transactions.ts b/x-pack/test/apm_api_integration/trial/tests/correlations/slow_transactions.ts deleted file mode 100644 index 9a868373292f6..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/correlations/slow_transactions.ts +++ /dev/null @@ -1,95 +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 { format } from 'url'; -import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; -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 range = archives_metadata[archiveName]; - - describe('Slow durations', () => { - const url = format({ - pathname: `/api/apm/correlations/slow_transactions`, - query: { - start: range.start, - end: range.end, - durationPercentile: 95, - fieldNames: - 'user.username,user.id,host.ip,user_agent.name,kubernetes.pod.uuid,url.domain,container.id,service.node.name', - }, - }); - - describe('when data is not loaded', () => { - it('handles the empty state', async () => { - const response = await supertest.get(url); - - expect(response.status).to.be(200); - expect(response.body.response).to.be(undefined); - }); - }); - - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - describe('making request with default args', () => { - type ResponseBody = APIReturnType<'GET /api/apm/correlations/slow_transactions'>; - let response: { - status: number; - body: NonNullable; - }; - - before(async () => { - response = await supertest.get(url); - }); - - it('returns successfully', () => { - expect(response.status).to.eql(200); - }); - - it('returns significant terms', () => { - const sorted = response.body?.significantTerms?.sort(); - expectSnapshot(sorted?.map((term) => term.fieldName)).toMatchInline(` - Array [ - "user_agent.name", - "url.domain", - "host.ip", - "service.node.name", - "container.id", - "url.domain", - "user_agent.name", - ] - `); - }); - - it('returns a distribution per term', () => { - expectSnapshot(response.body?.significantTerms?.map((term) => term.distribution.length)) - .toMatchInline(` - Array [ - 11, - 11, - 11, - 11, - 11, - 11, - 11, - ] - `); - }); - - it('returns overall distribution', () => { - expectSnapshot(response.body?.overall?.distribution.length).toMatchInline(`11`); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts b/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts deleted file mode 100644 index 05c6439508ece..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/csm_services.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function rumServicesApiTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('CSM Services', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/services?start=2020-06-28T10%3A24%3A46.055Z&end=2020-07-29T10%3A24%3A46.055Z&uiFilters=%7B%22agentName%22%3A%5B%22js-base%22%2C%22rum-js%22%5D%7D' - ); - - expect(response.status).to.be(200); - expect(response.body).to.eql([]); - }); - }); - - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - - it('returns rum services list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/services?start=2020-06-28T10%3A24%3A46.055Z&end=2020-07-29T10%3A24%3A46.055Z&uiFilters=%7B%22agentName%22%3A%5B%22js-base%22%2C%22rum-js%22%5D%7D' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatchInline(`Array []`); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts b/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts deleted file mode 100644 index fa5fcbcb6a7c3..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/page_load_dist.ts +++ /dev/null @@ -1,64 +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 { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function rumServicesApiTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('UX page load dist', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatch(); - }); - it('returns empty list with breakdowns', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdown=Browser' - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatch(); - }); - }); - - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - - it('returns page load distribution', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-load-distribution?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatch(); - }); - it('returns page load distribution with breakdown', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-load-distribution/breakdown?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdown=Browser' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatch(); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts b/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts deleted file mode 100644 index 5d910862843d5..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/page_views.ts +++ /dev/null @@ -1,64 +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 { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function rumServicesApiTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('CSM page views', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D' - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatch(); - }); - it('returns empty list with breakdowns', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D' - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatch(); - }); - }); - - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - - it('returns page views', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatch(); - }); - it('returns page views with breakdown', async () => { - const response = await supertest.get( - '/api/apm/rum-client/page-view-trends?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&breakdowns=%7B%22name%22%3A%22Browser%22%2C%22fieldName%22%3A%22user_agent.name%22%2C%22type%22%3A%22category%22%7D' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatch(); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts b/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts deleted file mode 100644 index 961c783434639..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/csm/url_search.ts +++ /dev/null @@ -1,89 +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 { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function rumServicesApiTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - describe('CSM url search api', () => { - describe('when there is no data', () => { - it('returns empty list', async () => { - const response = await supertest.get( - '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-14T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22elastic-co-rum-test%22%5D%7D&percentile=50' - ); - - expect(response.status).to.be(200); - expectSnapshot(response.body).toMatchInline(` - Object { - "items": Array [], - "total": 0, - } - `); - }); - }); - - describe('when there is data', () => { - before(async () => { - await esArchiver.load('8.0.0'); - await esArchiver.load('rum_8.0.0'); - }); - after(async () => { - await esArchiver.unload('8.0.0'); - await esArchiver.unload('rum_8.0.0'); - }); - - it('returns top urls when no query', async () => { - const response = await supertest.get( - '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&percentile=50' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatchInline(` - Object { - "items": Array [ - Object { - "count": 5, - "pld": 4924000, - "url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0", - }, - Object { - "count": 1, - "pld": 2760000, - "url": "http://localhost:5601/nfw/app/home", - }, - ], - "total": 2, - } - `); - }); - - it('returns specific results against query', async () => { - const response = await supertest.get( - '/api/apm/rum-client/url-search?start=2020-09-07T20%3A35%3A54.654Z&end=2020-09-16T20%3A35%3A54.654Z&uiFilters=%7B%22serviceName%22%3A%5B%22kibana-frontend-8_0_0%22%5D%7D&urlQuery=csm&percentile=50' - ); - - expect(response.status).to.be(200); - - expectSnapshot(response.body).toMatchInline(` - Object { - "items": Array [ - Object { - "count": 5, - "pld": 4924000, - "url": "http://localhost:5601/nfw/app/csm?rangeFrom=now-15m&rangeTo=now&serviceName=kibana-frontend-8_0_0", - }, - ], - "total": 1, - } - `); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts deleted file mode 100644 index c8ee858d9ceb0..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ /dev/null @@ -1,50 +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 { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; - -export default function observabilityApiIntegrationTests({ loadTestFile }: FtrProviderContext) { - describe('APM specs (trial)', function () { - this.tags('ciGroup1'); - - describe('Services', function () { - loadTestFile(require.resolve('./services/annotations')); - loadTestFile(require.resolve('./services/top_services.ts')); - }); - - describe('Transactions', function () { - loadTestFile(require.resolve('./transactions/latency')); - }); - - describe('Settings', function () { - loadTestFile(require.resolve('./settings/custom_link.ts')); - describe('Anomaly detection', function () { - loadTestFile(require.resolve('./settings/anomaly_detection/no_access_user')); - loadTestFile(require.resolve('./settings/anomaly_detection/read_user')); - loadTestFile(require.resolve('./settings/anomaly_detection/write_user')); - }); - }); - - describe('Service Maps', function () { - loadTestFile(require.resolve('./service_maps/service_maps')); - }); - - describe('CSM', function () { - loadTestFile(require.resolve('./csm/csm_services.ts')); - loadTestFile(require.resolve('./csm/web_core_vitals.ts')); - loadTestFile(require.resolve('./csm/long_task_metrics.ts')); - loadTestFile(require.resolve('./csm/url_search.ts')); - loadTestFile(require.resolve('./csm/page_views.ts')); - loadTestFile(require.resolve('./csm/js_errors.ts')); - loadTestFile(require.resolve('./csm/has_rum_data.ts')); - loadTestFile(require.resolve('./csm/page_load_dist.ts')); - }); - - describe('Correlations', function () { - loadTestFile(require.resolve('./correlations/slow_transactions')); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts deleted file mode 100644 index e37d98b41b7af..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts +++ /dev/null @@ -1,131 +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 { sortBy } from 'lodash'; -import { APIReturnType } from '../../../../../plugins/apm/public/services/rest/createCallApmApi'; -import { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const supertestAsApmReadUserWithoutMlAccess = getService('supertestAsApmReadUserWithoutMlAccess'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - - const range = archives_metadata[archiveName]; - - // url parameters - const start = encodeURIComponent(range.start); - const end = encodeURIComponent(range.end); - - const uiFilters = encodeURIComponent(JSON.stringify({})); - - describe('APM Services Overview', () => { - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - describe('with the default APM read user', () => { - describe('and fetching a list of services', () => { - let response: { - status: number; - body: APIReturnType<'GET /api/apm/services'>; - }; - - before(async () => { - response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - }); - - it('the response is successful', () => { - expect(response.status).to.eql(200); - }); - - it('there is at least one service', () => { - expect(response.body.items.length).to.be.greaterThan(0); - }); - - it('some items have a health status set', () => { - // Under the assumption that the loaded archive has - // at least one APM ML job, and the time range is longer - // than 15m, at least one items should have a health status - // set. Note that we currently have a bug where healthy - // services report as unknown (so without any health status): - // https://github.com/elastic/kibana/issues/77083 - - const healthStatuses = sortBy(response.body.items, 'serviceName').map( - (item: any) => item.healthStatus - ); - - expect(healthStatuses.filter(Boolean).length).to.be.greaterThan(0); - - expectSnapshot(healthStatuses).toMatchInline(` - Array [ - "healthy", - "healthy", - "healthy", - "healthy", - "healthy", - "healthy", - "healthy", - "healthy", - "healthy", - ] - `); - }); - }); - }); - - describe('with a user that does not have access to ML', () => { - let response: PromiseReturnType; - before(async () => { - response = await supertestAsApmReadUserWithoutMlAccess.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${uiFilters}` - ); - }); - - it('the response is successful', () => { - expect(response.status).to.eql(200); - }); - - it('there is at least one service', () => { - expect(response.body.items.length).to.be.greaterThan(0); - }); - - it('contains no health statuses', () => { - const definedHealthStatuses = response.body.items - .map((item: any) => item.healthStatus) - .filter(Boolean); - - expect(definedHealthStatuses.length).to.be(0); - }); - }); - - describe('and fetching a list of services with a filter', () => { - let response: PromiseReturnType; - before(async () => { - response = await supertest.get( - `/api/apm/services?start=${start}&end=${end}&uiFilters=${encodeURIComponent( - `{"kuery":"service.name:opbeans-java","environment":"ENVIRONMENT_ALL"}` - )}` - ); - }); - - it('does not return health statuses for services that are not found in APM data', () => { - expect(response.status).to.be(200); - - expect(response.body.items.length).to.be(1); - - expect(response.body.items[0].serviceName).to.be('opbeans-java'); - }); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts deleted file mode 100644 index a917bdb3cea23..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/no_access_user.ts +++ /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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const noAccessUser = getService('supertestAsNoAccessUser'); - - function getJobs() { - return noAccessUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createJobs(environments: string[]) { - return noAccessUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - describe('when user does not have read access to ML', () => { - describe('when calling the endpoint for listing jobs', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await getJobs(); - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - - describe('when calling create endpoint', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await createJobs(['production', 'staging']); - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.ts deleted file mode 100644 index 2265c4dc0a41d..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/read_user.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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const apmReadUser = getService('supertestAsApmReadUser'); - - function getJobs() { - return apmReadUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createJobs(environments: string[]) { - return apmReadUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - describe('when user has read access to ML', () => { - describe('when calling the endpoint for listing jobs', () => { - it('returns a list of jobs', async () => { - const { body } = await getJobs(); - - expect(body.jobs.length).to.be(0); - expect(body.hasLegacyJobs).to.be(false); - }); - }); - - describe('when calling create endpoint', () => { - it('returns an error because the user does not have access', async () => { - const { body } = await createJobs(['production', 'staging']); - - expect(body.statusCode).to.be(403); - expect(body.error).to.be('Forbidden'); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts b/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.ts deleted file mode 100644 index 720d66e1efcc8..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/settings/anomaly_detection/write_user.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 expect from '@kbn/expect'; -import { FtrProviderContext } from '../../../../common/ftr_provider_context'; - -export default function apiTest({ getService }: FtrProviderContext) { - const apmWriteUser = getService('supertestAsApmWriteUser'); - - function getJobs() { - return apmWriteUser.get(`/api/apm/settings/anomaly-detection/jobs`).set('kbn-xsrf', 'foo'); - } - - function createJobs(environments: string[]) { - return apmWriteUser - .post(`/api/apm/settings/anomaly-detection/jobs`) - .send({ environments }) - .set('kbn-xsrf', 'foo'); - } - - function deleteJobs(jobIds: string[]) { - return apmWriteUser.post(`/api/ml/jobs/delete_jobs`).send({ jobIds }).set('kbn-xsrf', 'foo'); - } - - describe('when user has write access to ML', () => { - after(async () => { - const res = await getJobs(); - const jobIds = res.body.jobs.map((job: any) => job.job_id); - await deleteJobs(jobIds); - }); - - describe('when calling the endpoint for listing jobs', () => { - it('returns a list of jobs', async () => { - const { body } = await getJobs(); - expect(body.jobs.length).to.be(0); - expect(body.hasLegacyJobs).to.be(false); - }); - }); - - describe('when calling create endpoint', () => { - it('creates two jobs', async () => { - await createJobs(['production', 'staging']); - - const { body } = await getJobs(); - expect(body.hasLegacyJobs).to.be(false); - expect(body.jobs.map((job: any) => job.environment)).to.eql(['production', 'staging']); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/settings/custom_link.ts b/x-pack/test/apm_api_integration/trial/tests/settings/custom_link.ts deleted file mode 100644 index bcfe8fce4b948..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/settings/custom_link.ts +++ /dev/null @@ -1,159 +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 URL from 'url'; -import expect from '@kbn/expect'; -import { CustomLink } from '../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -export default function customLinksTests({ getService }: FtrProviderContext) { - const supertestRead = getService('supertest'); - const supertestWrite = getService('supertestAsApmWriteUser'); - const log = getService('log'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - - function searchCustomLinks(filters?: any) { - const path = URL.format({ - pathname: `/api/apm/settings/custom_links`, - query: filters, - }); - return supertestRead.get(path).set('kbn-xsrf', 'foo'); - } - - async function createCustomLink(customLink: CustomLink) { - log.debug('creating configuration', customLink); - const res = await supertestWrite - .post(`/api/apm/settings/custom_links`) - .send(customLink) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - async function updateCustomLink(id: string, customLink: CustomLink) { - log.debug('updating configuration', id, customLink); - const res = await supertestWrite - .put(`/api/apm/settings/custom_links/${id}`) - .send(customLink) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - async function deleteCustomLink(id: string) { - log.debug('deleting configuration', id); - const res = await supertestWrite - .delete(`/api/apm/settings/custom_links/${id}`) - .set('kbn-xsrf', 'foo'); - - throwOnError(res); - - return res; - } - - function throwOnError(res: any) { - const { statusCode, req, body } = res; - if (statusCode !== 200) { - throw new Error(` - Endpoint: ${req.method} ${req.path} - Service: ${JSON.stringify(res.request._data.service)} - Status code: ${statusCode} - Response: ${body.message}`); - } - } - - describe('custom links', () => { - before(async () => { - const customLink = { - url: 'https://elastic.co', - label: 'with filters', - filters: [ - { key: 'service.name', value: 'baz' }, - { key: 'transaction.type', value: 'qux' }, - ], - } as CustomLink; - await createCustomLink(customLink); - }); - it('fetches a custom link', async () => { - const { status, body } = await searchCustomLinks({ - 'service.name': 'baz', - 'transaction.type': 'qux', - }); - const { label, url, filters } = body[0]; - - expect(status).to.equal(200); - expect({ label, url, filters }).to.eql({ - label: 'with filters', - url: 'https://elastic.co', - filters: [ - { key: 'service.name', value: 'baz' }, - { key: 'transaction.type', value: 'qux' }, - ], - }); - }); - it('updates a custom link', async () => { - let { status, body } = await searchCustomLinks({ - 'service.name': 'baz', - 'transaction.type': 'qux', - }); - expect(status).to.equal(200); - await updateCustomLink(body[0].id, { - label: 'foo', - url: 'https://elastic.co?service.name={{service.name}}', - filters: [ - { key: 'service.name', value: 'quz' }, - { key: 'transaction.name', value: 'bar' }, - ], - }); - ({ status, body } = await searchCustomLinks({ - 'service.name': 'quz', - 'transaction.name': 'bar', - })); - const { label, url, filters } = body[0]; - expect(status).to.equal(200); - expect({ label, url, filters }).to.eql({ - label: 'foo', - url: 'https://elastic.co?service.name={{service.name}}', - filters: [ - { key: 'service.name', value: 'quz' }, - { key: 'transaction.name', value: 'bar' }, - ], - }); - }); - it('deletes a custom link', async () => { - let { status, body } = await searchCustomLinks({ - 'service.name': 'quz', - 'transaction.name': 'bar', - }); - expect(status).to.equal(200); - await deleteCustomLink(body[0].id); - ({ status, body } = await searchCustomLinks({ - 'service.name': 'quz', - 'transaction.name': 'bar', - })); - expect(status).to.equal(200); - expect(body).to.eql([]); - }); - - describe('transaction', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - it('fetches a transaction sample', async () => { - const response = await supertestRead.get( - '/api/apm/settings/custom_links/transaction?service.name=opbeans-java' - ); - expect(response.status).to.be(200); - expect(response.body.service.name).to.eql('opbeans-java'); - }); - }); - }); -} diff --git a/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap b/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap deleted file mode 100644 index 9475670387a08..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/transactions/__snapshots__/latency.snap +++ /dev/null @@ -1,46 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters when not defined environments seleted should return the correct anomaly boundaries 1`] = ` -Array [ - Object { - "x": 1607436000000, - "y": 0, - "y0": 0, - }, - Object { - "x": 1607436900000, - "y": 0, - "y0": 0, - }, -] -`; - -exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` -Array [ - Object { - "x": 1607436000000, - "y": 1625128.56211579, - "y0": 7533.02707532227, - }, - Object { - "x": 1607436900000, - "y": 1660982.24115757, - "y0": 5732.00699123528, - }, -] -`; - -exports[`APM specs (trial) Transactions Latency when data is loaded and fetching transaction charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` -Array [ - Object { - "x": 1607436000000, - "y": 1625128.56211579, - "y0": 7533.02707532227, - }, - Object { - "x": 1607436900000, - "y": 1660982.24115757, - "y0": 5732.00699123528, - }, -] -`; diff --git a/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts b/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts deleted file mode 100644 index e0b9559be7208..0000000000000 --- a/x-pack/test/apm_api_integration/trial/tests/transactions/latency.ts +++ /dev/null @@ -1,159 +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 { PromiseReturnType } from '../../../../../plugins/observability/typings/common'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; -import archives_metadata from '../../../common/fixtures/es_archiver/archives_metadata'; - -export default function ApiTest({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const esArchiver = getService('esArchiver'); - - const archiveName = 'apm_8.0.0'; - - const range = archives_metadata[archiveName]; - - // url parameters - const start = encodeURIComponent(range.start); - const end = encodeURIComponent(range.end); - const transactionType = 'request'; - - describe('Latency', () => { - describe('when data is loaded', () => { - before(() => esArchiver.load(archiveName)); - after(() => esArchiver.unload(archiveName)); - - describe('and fetching transaction charts with uiFilters', () => { - let response: PromiseReturnType; - - describe('without environment', () => { - const uiFilters = encodeURIComponent(JSON.stringify({})); - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - }); - it('should return an error response', () => { - expect(response.status).to.eql(400); - }); - }); - - describe('without uiFilters', () => { - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&latencyAggregationType=avg` - ); - }); - it('should return an error response', () => { - expect(response.status).to.eql(400); - }); - }); - - describe('with environment selected in uiFilters', () => { - const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' })); - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - }); - - it('should have a successful response', () => { - expect(response.status).to.eql(200); - }); - - it('should return the ML job id for anomalies of the selected environment', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expect(response.body.anomalyTimeseries).to.have.property('jobId'); - expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( - `"apm-production-1369-high_mean_transaction_duration"` - ); - }); - - it('should return a non-empty anomaly series', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); - expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); - }); - }); - - describe('when not defined environments seleted', () => { - const uiFilters = encodeURIComponent( - JSON.stringify({ environment: 'ENVIRONMENT_NOT_DEFINED' }) - ); - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-python/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - }); - - it('should have a successful response', () => { - expect(response.status).to.eql(200); - }); - - it('should return the ML job id for anomalies with no defined environment', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expect(response.body.anomalyTimeseries).to.have.property('jobId'); - expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( - `"apm-environment_not_defined-5626-high_mean_transaction_duration"` - ); - }); - - it('should return the correct anomaly boundaries', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); - }); - }); - - describe('with all environments selected', () => { - const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' })); - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - }); - - it('should have a successful response', () => { - expect(response.status).to.eql(200); - }); - - it('should not return anomaly timeseries data', () => { - expect(response.body).to.not.have.property('anomalyTimeseries'); - }); - }); - - describe('with environment selected and empty kuery filter', () => { - const uiFilters = encodeURIComponent( - JSON.stringify({ kuery: '', environment: 'production' }) - ); - before(async () => { - response = await supertest.get( - `/api/apm/services/opbeans-java/transactions/charts/latency?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}&latencyAggregationType=avg` - ); - }); - - it('should have a successful response', () => { - expect(response.status).to.eql(200); - }); - - it('should return the ML job id for anomalies of the selected environment', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expect(response.body.anomalyTimeseries).to.have.property('jobId'); - expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( - `"apm-production-1369-high_mean_transaction_duration"` - ); - }); - - it('should return a non-empty anomaly series', () => { - expect(response.body).to.have.property('anomalyTimeseries'); - expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); - expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); - }); - }); - }); - }); - }); -} From 64e9cf0440e1e60b25a8b04939cb9d0097efcb53 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Jan 2021 12:45:49 +0200 Subject: [PATCH 76/90] Cleanup OSS code from visualizations wizard (#89092) * Cleanup OSS code from visualizations wizard * Remove unecessary translations * Remove unused translation * Fix functional test * Disable the functional test for OSS * Remove from oss correctly :D * Fix ci --- .github/CODEOWNERS | 2 - .i18nrc.json | 2 - docs/developer/plugin-list.asciidoc | 10 --- .../deprecation/deprecation_factory.test.ts | 85 +------------------ .../src/deprecation/deprecation_factory.ts | 26 ------ packages/kbn-config/src/deprecation/index.ts | 2 +- packages/kbn-config/src/index.ts | 1 - packages/kbn-optimizer/limits.yml | 2 - src/plugins/lens_oss/README.md | 6 -- src/plugins/lens_oss/common/constants.ts | 12 --- src/plugins/lens_oss/common/index.ts | 9 -- src/plugins/lens_oss/config.ts | 15 ---- src/plugins/lens_oss/kibana.json | 10 --- src/plugins/lens_oss/public/index.ts | 11 --- src/plugins/lens_oss/public/plugin.ts | 32 ------- src/plugins/lens_oss/public/vis_type_alias.ts | 37 -------- src/plugins/lens_oss/server/index.ts | 21 ----- src/plugins/lens_oss/tsconfig.json | 20 ----- src/plugins/maps_oss/README.md | 6 -- src/plugins/maps_oss/common/constants.ts | 12 --- src/plugins/maps_oss/common/index.ts | 9 -- src/plugins/maps_oss/config.ts | 15 ---- src/plugins/maps_oss/kibana.json | 10 --- src/plugins/maps_oss/public/index.ts | 11 --- src/plugins/maps_oss/public/plugin.ts | 32 ------- src/plugins/maps_oss/public/vis_type_alias.ts | 33 ------- src/plugins/maps_oss/server/index.ts | 21 ----- .../vis_types/vis_type_alias_registry.ts | 7 -- .../group_selection/group_selection.test.tsx | 40 --------- .../group_selection/group_selection.tsx | 26 ------ .../functional/apps/visualize/_chart_types.ts | 18 +--- test/functional/apps/visualize/index.ts | 6 +- tsconfig.json | 2 - tsconfig.refs.json | 1 - x-pack/plugins/lens/kibana.json | 2 +- x-pack/plugins/lens/public/plugin.ts | 3 - x-pack/plugins/lens/tsconfig.json | 1 - x-pack/plugins/maps/kibana.json | 2 +- x-pack/plugins/maps/public/plugin.ts | 4 - .../translations/translations/ja-JP.json | 8 -- .../translations/translations/zh-CN.json | 8 -- 41 files changed, 10 insertions(+), 570 deletions(-) delete mode 100644 src/plugins/lens_oss/README.md delete mode 100644 src/plugins/lens_oss/common/constants.ts delete mode 100644 src/plugins/lens_oss/common/index.ts delete mode 100644 src/plugins/lens_oss/config.ts delete mode 100644 src/plugins/lens_oss/kibana.json delete mode 100644 src/plugins/lens_oss/public/index.ts delete mode 100644 src/plugins/lens_oss/public/plugin.ts delete mode 100644 src/plugins/lens_oss/public/vis_type_alias.ts delete mode 100644 src/plugins/lens_oss/server/index.ts delete mode 100644 src/plugins/lens_oss/tsconfig.json delete mode 100644 src/plugins/maps_oss/README.md delete mode 100644 src/plugins/maps_oss/common/constants.ts delete mode 100644 src/plugins/maps_oss/common/index.ts delete mode 100644 src/plugins/maps_oss/config.ts delete mode 100644 src/plugins/maps_oss/kibana.json delete mode 100644 src/plugins/maps_oss/public/index.ts delete mode 100644 src/plugins/maps_oss/public/plugin.ts delete mode 100644 src/plugins/maps_oss/public/vis_type_alias.ts delete mode 100644 src/plugins/maps_oss/server/index.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b47d78ea6d691..0630937d5ac4b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,7 +13,6 @@ /src/plugins/advanced_settings/ @elastic/kibana-app /src/plugins/charts/ @elastic/kibana-app /src/plugins/discover/ @elastic/kibana-app -/src/plugins/lens_oss/ @elastic/kibana-app /src/plugins/management/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app /src/plugins/timelion/ @elastic/kibana-app @@ -127,7 +126,6 @@ /x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis /x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis #CC# /src/plugins/maps_legacy/ @elastic/kibana-gis -#CC# /src/plugins/maps_oss/ @elastic/kibana-gis #CC# /x-pack/plugins/file_upload @elastic/kibana-gis #CC# /x-pack/plugins/maps_legacy_licensing @elastic/kibana-gis /src/plugins/tile_map/ @elastic/kibana-gis diff --git a/.i18nrc.json b/.i18nrc.json index b425dd99857dc..0cdcae08e54e0 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -61,8 +61,6 @@ "visTypeVislib": "src/plugins/vis_type_vislib", "visTypeXy": "src/plugins/vis_type_xy", "visualizations": "src/plugins/visualizations", - "lensOss": "src/plugins/lens_oss", - "mapsOss": "src/plugins/maps_oss", "visualize": "src/plugins/visualize", "apmOss": "src/plugins/apm_oss", "usageCollection": "src/plugins/usage_collection" diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 7ce4896a8bce4..fd4ed75352b1f 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -130,11 +130,6 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel. |The legacyExport plugin adds support for the legacy saved objects export format. -|{kib-repo}blob/{branch}/src/plugins/lens_oss/README.md[lensOss] -|The lens_oss plugin registers the lens visualization on OSS. -It is registered as disabled. The x-pack plugin should unregister this. - - |{kib-repo}blob/{branch}/src/plugins/management/README.md[management] |This plugins contains the "Stack Management" page framework. It offers navigation and an API to link individual managment section into it. This plugin does not contain any individual @@ -145,11 +140,6 @@ management section itself. |Internal objects used by the Coordinate, Region, and Vega visualizations. -|{kib-repo}blob/{branch}/src/plugins/maps_oss/README.md[mapsOss] -|The maps_oss plugin registers the maps visualization on OSS. -It is registered as disabled. The x-pack plugin should unregister this. - - |{kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation] |The navigation plugins exports the TopNavMenu component. It also provides a stateful version of it on the start contract. diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts index 5184494dd74bf..1c13c539c3746 100644 --- a/packages/kbn-config/src/deprecation/deprecation_factory.test.ts +++ b/packages/kbn-config/src/deprecation/deprecation_factory.test.ts @@ -7,7 +7,7 @@ */ import { ConfigDeprecationLogger } from './types'; -import { configDeprecationFactory, copyFromRoot } from './deprecation_factory'; +import { configDeprecationFactory } from './deprecation_factory'; describe('DeprecationFactory', () => { const { rename, unused, renameFromRoot, unusedFromRoot } = configDeprecationFactory; @@ -239,89 +239,6 @@ describe('DeprecationFactory', () => { }); }); - describe('copyFromRoot', () => { - it('copies a property to a different namespace', () => { - const rawConfig = { - originplugin: { - deprecated: 'toberenamed', - valid: 'valid', - }, - destinationplugin: { - property: 'value', - }, - }; - const processed = copyFromRoot('originplugin.deprecated', 'destinationplugin.renamed')( - rawConfig, - 'does-not-matter', - logger - ); - expect(processed).toEqual({ - originplugin: { - deprecated: 'toberenamed', - valid: 'valid', - }, - destinationplugin: { - renamed: 'toberenamed', - property: 'value', - }, - }); - expect(deprecationMessages.length).toEqual(0); - }); - - it('does not alter config if origin property is not present', () => { - const rawConfig = { - myplugin: { - new: 'new', - valid: 'valid', - }, - someOtherPlugin: { - property: 'value', - }, - }; - const processed = copyFromRoot('myplugin.deprecated', 'myplugin.new')( - rawConfig, - 'does-not-matter', - logger - ); - expect(processed).toEqual({ - myplugin: { - new: 'new', - valid: 'valid', - }, - someOtherPlugin: { - property: 'value', - }, - }); - expect(deprecationMessages.length).toEqual(0); - }); - - it('does not alter config if they both exist', () => { - const rawConfig = { - myplugin: { - deprecated: 'deprecated', - renamed: 'renamed', - }, - someOtherPlugin: { - property: 'value', - }, - }; - const processed = copyFromRoot('myplugin.deprecated', 'someOtherPlugin.property')( - rawConfig, - 'does-not-matter', - logger - ); - expect(processed).toEqual({ - myplugin: { - deprecated: 'deprecated', - renamed: 'renamed', - }, - someOtherPlugin: { - property: 'value', - }, - }); - }); - }); - describe('unused', () => { it('removes the unused property from the config and logs a warning is present', () => { const rawConfig = { diff --git a/packages/kbn-config/src/deprecation/deprecation_factory.ts b/packages/kbn-config/src/deprecation/deprecation_factory.ts index 40e29f9739156..04826446dc1ad 100644 --- a/packages/kbn-config/src/deprecation/deprecation_factory.ts +++ b/packages/kbn-config/src/deprecation/deprecation_factory.ts @@ -45,26 +45,6 @@ const _rename = ( return config; }; -const _copy = ( - config: Record, - rootPath: string, - originKey: string, - destinationKey: string -) => { - const originPath = getPath(rootPath, originKey); - const originValue = get(config, originPath); - if (originValue === undefined) { - return config; - } - - const destinationPath = getPath(rootPath, destinationKey); - const destinationValue = get(config, destinationPath); - if (destinationValue === undefined) { - set(config, destinationPath, originValue); - } - return config; -}; - const _unused = ( config: Record, rootPath: string, @@ -89,12 +69,6 @@ const renameFromRoot = (oldKey: string, newKey: string, silent?: boolean): Confi log ) => _rename(config, '', log, oldKey, newKey, silent); -export const copyFromRoot = (originKey: string, destinationKey: string): ConfigDeprecation => ( - config, - rootPath, - log -) => _copy(config, '', originKey, destinationKey); - const unused = (unusedKey: string): ConfigDeprecation => (config, rootPath, log) => _unused(config, rootPath, log, unusedKey); diff --git a/packages/kbn-config/src/deprecation/index.ts b/packages/kbn-config/src/deprecation/index.ts index 87ec43d1fb09f..6deebd27399bb 100644 --- a/packages/kbn-config/src/deprecation/index.ts +++ b/packages/kbn-config/src/deprecation/index.ts @@ -13,5 +13,5 @@ export { ConfigDeprecationFactory, ConfigDeprecationProvider, } from './types'; -export { configDeprecationFactory, copyFromRoot } from './deprecation_factory'; +export { configDeprecationFactory } from './deprecation_factory'; export { applyDeprecations } from './apply_deprecations'; diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index e1743270f62c7..a803e3fd2dc8e 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -14,7 +14,6 @@ export { ConfigDeprecationLogger, ConfigDeprecationProvider, ConfigDeprecationWithContext, - copyFromRoot, } from './deprecation'; export { RawConfigurationProvider, RawConfigService, getConfigFromFiles } from './raw'; diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ef672d6cbeb2e..1a4fb390d0c17 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -44,7 +44,6 @@ pageLoadAssetSize: kibanaReact: 161921 kibanaUtils: 198829 lens: 96624 - lensOss: 19341 licenseManagement: 41817 licensing: 39008 lists: 202261 @@ -53,7 +52,6 @@ pageLoadAssetSize: maps: 183610 mapsLegacy: 116817 mapsLegacyLicensing: 20214 - mapsOss: 19284 ml: 82187 monitoring: 50000 navigation: 37269 diff --git a/src/plugins/lens_oss/README.md b/src/plugins/lens_oss/README.md deleted file mode 100644 index 187da2497026e..0000000000000 --- a/src/plugins/lens_oss/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# lens_oss - -The lens_oss plugin registers the lens visualization on OSS. -It is registered as disabled. The x-pack plugin should unregister this. - -`visualizations.unregisterAlias('lensOss')` \ No newline at end of file diff --git a/src/plugins/lens_oss/common/constants.ts b/src/plugins/lens_oss/common/constants.ts deleted file mode 100644 index 0ff5cdd78bb1b..0000000000000 --- a/src/plugins/lens_oss/common/constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export const APP_NAME = 'lens'; -export const PLUGIN_ID_OSS = 'lensOss'; -export const APP_PATH = '#/'; -export const APP_ICON = 'lensApp'; diff --git a/src/plugins/lens_oss/common/index.ts b/src/plugins/lens_oss/common/index.ts deleted file mode 100644 index 7f60b8508dc27..0000000000000 --- a/src/plugins/lens_oss/common/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export * from './constants'; diff --git a/src/plugins/lens_oss/config.ts b/src/plugins/lens_oss/config.ts deleted file mode 100644 index 58c50f0104f46..0000000000000 --- a/src/plugins/lens_oss/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; - -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), -}); - -export type ConfigSchema = TypeOf; diff --git a/src/plugins/lens_oss/kibana.json b/src/plugins/lens_oss/kibana.json deleted file mode 100644 index 3e3d3585f37fb..0000000000000 --- a/src/plugins/lens_oss/kibana.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "lensOss", - "version": "kibana", - "ui": true, - "server": true, - "requiredPlugins": [ - "visualizations" - ], - "extraPublicDirs": ["common/constants"] -} diff --git a/src/plugins/lens_oss/public/index.ts b/src/plugins/lens_oss/public/index.ts deleted file mode 100644 index 2f68f6d183a22..0000000000000 --- a/src/plugins/lens_oss/public/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { LensOSSPlugin } from './plugin'; - -export const plugin = () => new LensOSSPlugin(); diff --git a/src/plugins/lens_oss/public/plugin.ts b/src/plugins/lens_oss/public/plugin.ts deleted file mode 100644 index 5a441614b7e24..0000000000000 --- a/src/plugins/lens_oss/public/plugin.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { DocLinksStart, CoreSetup } from 'src/core/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { getLensAliasConfig } from './vis_type_alias'; - -export interface LensPluginSetupDependencies { - visualizations: VisualizationsSetup; -} - -export interface LensPluginStartDependencies { - docLinks: DocLinksStart; -} - -export class LensOSSPlugin { - setup( - core: CoreSetup, - { visualizations }: LensPluginSetupDependencies - ) { - core.getStartServices().then(([coreStart]) => { - visualizations.registerAlias(getLensAliasConfig(coreStart.docLinks)); - }); - } - - start() {} -} diff --git a/src/plugins/lens_oss/public/vis_type_alias.ts b/src/plugins/lens_oss/public/vis_type_alias.ts deleted file mode 100644 index b9806bbf3b4e5..0000000000000 --- a/src/plugins/lens_oss/public/vis_type_alias.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import { VisTypeAlias } from 'src/plugins/visualizations/public'; -import { DocLinksStart } from 'src/core/public'; -import { APP_NAME, PLUGIN_ID_OSS, APP_PATH, APP_ICON } from '../common'; - -export const getLensAliasConfig = ({ links }: DocLinksStart): VisTypeAlias => ({ - aliasPath: APP_PATH, - aliasApp: APP_NAME, - name: PLUGIN_ID_OSS, - title: i18n.translate('lensOss.visTypeAlias.title', { - defaultMessage: 'Lens', - }), - description: i18n.translate('lensOss.visTypeAlias.description', { - defaultMessage: - 'Create visualizations with our drag-and-drop editor. Switch between visualization types at any time. Best for most visualizations.', - }), - icon: APP_ICON, - stage: 'production', - disabled: true, - note: i18n.translate('lensOss.visTypeAlias.note', { - defaultMessage: 'Recommended for most users.', - }), - promoTooltip: { - description: i18n.translate('lensOss.visTypeAlias.promoTooltip.description', { - defaultMessage: 'Try Lens for free with Elastic. Learn more.', - }), - link: `${links.visualize.lens}?blade=kibanaossvizwizard`, - }, -}); diff --git a/src/plugins/lens_oss/server/index.ts b/src/plugins/lens_oss/server/index.ts deleted file mode 100644 index d13a9b2caaeb2..0000000000000 --- a/src/plugins/lens_oss/server/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { PluginConfigDescriptor } from 'kibana/server'; -import { copyFromRoot } from '@kbn/config'; -import { configSchema, ConfigSchema } from '../config'; - -export const config: PluginConfigDescriptor = { - schema: configSchema, - deprecations: () => [copyFromRoot('xpack.lens.enabled', 'lens_oss.enabled')], -}; - -export const plugin = () => ({ - setup() {}, - start() {}, -}); diff --git a/src/plugins/lens_oss/tsconfig.json b/src/plugins/lens_oss/tsconfig.json deleted file mode 100644 index d7bbc593fa87b..0000000000000 --- a/src/plugins/lens_oss/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "../../../tsconfig.base.json", - "compilerOptions": { - "composite": true, - "outDir": "./target/types", - "emitDeclarationOnly": true, - "declaration": true, - "declarationMap": true - }, - "include": [ - "common/**/*", - "public/**/*", - "server/**/*", - "*.ts" - ], - "references": [ - { "path": "../../core/tsconfig.json" }, - { "path": "../visualizations/tsconfig.json" } - ] -} diff --git a/src/plugins/maps_oss/README.md b/src/plugins/maps_oss/README.md deleted file mode 100644 index ed91de500fbfb..0000000000000 --- a/src/plugins/maps_oss/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# maps_oss - -The maps_oss plugin registers the maps visualization on OSS. -It is registered as disabled. The x-pack plugin should unregister this. - -`visualizations.unregisterAlias('mapsOss')` \ No newline at end of file diff --git a/src/plugins/maps_oss/common/constants.ts b/src/plugins/maps_oss/common/constants.ts deleted file mode 100644 index db29f541a03df..0000000000000 --- a/src/plugins/maps_oss/common/constants.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export const APP_NAME = 'maps'; -export const PLUGIN_ID_OSS = 'mapsOss'; -export const APP_PATH = '/map'; -export const APP_ICON = 'gisApp'; diff --git a/src/plugins/maps_oss/common/index.ts b/src/plugins/maps_oss/common/index.ts deleted file mode 100644 index 7f60b8508dc27..0000000000000 --- a/src/plugins/maps_oss/common/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -export * from './constants'; diff --git a/src/plugins/maps_oss/config.ts b/src/plugins/maps_oss/config.ts deleted file mode 100644 index 58c50f0104f46..0000000000000 --- a/src/plugins/maps_oss/config.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { schema, TypeOf } from '@kbn/config-schema'; - -export const configSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), -}); - -export type ConfigSchema = TypeOf; diff --git a/src/plugins/maps_oss/kibana.json b/src/plugins/maps_oss/kibana.json deleted file mode 100644 index 19770dcffaadd..0000000000000 --- a/src/plugins/maps_oss/kibana.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "id": "mapsOss", - "version": "kibana", - "ui": true, - "server": true, - "requiredPlugins": [ - "visualizations" - ], - "extraPublicDirs": ["common/constants"] -} diff --git a/src/plugins/maps_oss/public/index.ts b/src/plugins/maps_oss/public/index.ts deleted file mode 100644 index 1d27dc4b6d996..0000000000000 --- a/src/plugins/maps_oss/public/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { MapsOSSPlugin } from './plugin'; - -export const plugin = () => new MapsOSSPlugin(); diff --git a/src/plugins/maps_oss/public/plugin.ts b/src/plugins/maps_oss/public/plugin.ts deleted file mode 100644 index 5e27ae34257bf..0000000000000 --- a/src/plugins/maps_oss/public/plugin.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { DocLinksStart, CoreSetup } from 'src/core/public'; -import { VisualizationsSetup } from '../../visualizations/public'; -import { getMapsAliasConfig } from './vis_type_alias'; - -export interface MapsPluginSetupDependencies { - visualizations: VisualizationsSetup; -} - -export interface MapsPluginStartDependencies { - docLinks: DocLinksStart; -} - -export class MapsOSSPlugin { - setup( - core: CoreSetup, - { visualizations }: MapsPluginSetupDependencies - ) { - core.getStartServices().then(([coreStart]) => { - visualizations.registerAlias(getMapsAliasConfig(coreStart.docLinks)); - }); - } - - start() {} -} diff --git a/src/plugins/maps_oss/public/vis_type_alias.ts b/src/plugins/maps_oss/public/vis_type_alias.ts deleted file mode 100644 index a27c628755cf6..0000000000000 --- a/src/plugins/maps_oss/public/vis_type_alias.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 - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import { VisTypeAlias } from 'src/plugins/visualizations/public'; -import { DocLinksStart } from 'src/core/public'; -import { APP_NAME, PLUGIN_ID_OSS, APP_PATH, APP_ICON } from '../common'; - -export const getMapsAliasConfig = ({ links }: DocLinksStart): VisTypeAlias => ({ - aliasPath: APP_PATH, - aliasApp: APP_NAME, - name: PLUGIN_ID_OSS, - title: i18n.translate('mapsOss.visTypeAlias.title', { - defaultMessage: 'Maps', - }), - description: i18n.translate('mapsOss.visTypeAlias.description', { - defaultMessage: 'Plot and style your geo data in a multi layer map.', - }), - icon: APP_ICON, - stage: 'production', - disabled: true, - promoTooltip: { - description: i18n.translate('mapsOss.visTypeAlias.promoTooltip.description', { - defaultMessage: 'Try maps for free with Elastic. Learn more.', - }), - link: `${links.visualize.maps}?blade=kibanaossvizwizard`, - }, -}); diff --git a/src/plugins/maps_oss/server/index.ts b/src/plugins/maps_oss/server/index.ts deleted file mode 100644 index 8f07beee705a6..0000000000000 --- a/src/plugins/maps_oss/server/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { PluginConfigDescriptor } from 'kibana/server'; -import { copyFromRoot } from '@kbn/config'; -import { configSchema, ConfigSchema } from '../config'; - -export const config: PluginConfigDescriptor = { - schema: configSchema, - deprecations: () => [copyFromRoot('xpack.maps.enabled', 'maps_oss.enabled')], -}; - -export const plugin = () => ({ - setup() {}, - start() {}, -}); diff --git a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts index 839d2b1f57c34..cb6bc624801d9 100644 --- a/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts +++ b/src/plugins/visualizations/public/vis_types/vis_type_alias_registry.ts @@ -31,11 +31,6 @@ export interface VisualizationsAppExtension { toListItem: (savedObject: SavedObject) => VisualizationListItem; } -export interface VisTypeAliasPromoTooltip { - description: string; - link: string; -} - export interface VisTypeAlias { aliasPath: string; aliasApp: string; @@ -43,10 +38,8 @@ export interface VisTypeAlias { title: string; icon: string; promotion?: boolean; - promoTooltip?: VisTypeAliasPromoTooltip; description: string; note?: string; - disabled?: boolean; getSupportedTriggers?: () => string[]; stage: VisualizationStage; diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx index 74163296e31fd..396be30aca6d0 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.test.tsx @@ -39,11 +39,6 @@ describe('GroupSelection', () => { title: 'Vis with alias Url', aliasApp: 'aliasApp', aliasPath: '#/aliasApp', - disabled: true, - promoTooltip: { - description: 'Learn More', - link: '#/anotherUrl', - }, description: 'Vis with alias Url', stage: 'production', group: VisGroups.PROMOTED, @@ -227,41 +222,6 @@ describe('GroupSelection', () => { ]); }); - it('should render disabled aliases with a disabled class', () => { - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').exists()).toBe(true); - expect( - wrapper - .find('[data-test-subj="visType-visWithAliasUrl"]') - .at(1) - .hasClass('euiCard-isDisabled') - ).toBe(true); - }); - - it('should render a basic badge with link for disabled aliases with promoTooltip', () => { - const wrapper = mountWithIntl( - - ); - expect(wrapper.find('[data-test-subj="visTypeBadge"]').exists()).toBe(true); - expect(wrapper.find('[data-test-subj="visTypeBadge"]').at(0).prop('tooltipContent')).toBe( - 'Learn More' - ); - }); - it('should not show tools experimental visualizations if showExperimentalis false', () => { const expVis = { name: 'visExp', diff --git a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx index 9b61b2c415e9f..594e37f6a7608 100644 --- a/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx +++ b/src/plugins/visualizations/public/wizard/group_selection/group_selection.tsx @@ -48,10 +48,6 @@ interface VisCardProps { showExperimental?: boolean | undefined; } -function isVisTypeAlias(type: BaseVisType | VisTypeAlias): type is VisTypeAlias { - return 'aliasPath' in type; -} - function GroupSelection(props: GroupSelectionProps) { const visualizeGuideLink = props.docLinks.links.dashboard.guide; const promotedVisGroups = useMemo( @@ -185,29 +181,8 @@ const VisGroup = ({ visType, onVisTypeSelected }: VisCardProps) => { const onClick = useCallback(() => { onVisTypeSelected(visType); }, [onVisTypeSelected, visType]); - const shouldDisableCard = isVisTypeAlias(visType) && visType.disabled; - const betaBadgeContent = - shouldDisableCard && 'promoTooltip' in visType ? ( - - - - ) : undefined; return ( - {betaBadgeContent} { } onClick={onClick} - isDisabled={shouldDisableCard} data-test-subj={`visType-${visType.name}`} data-vis-stage={!('aliasPath' in visType) ? visType.stage : 'alias'} aria-label={`visType-${visType.name}`} diff --git a/test/functional/apps/visualize/_chart_types.ts b/test/functional/apps/visualize/_chart_types.ts index 55b68b7370148..69403f2090594 100644 --- a/test/functional/apps/visualize/_chart_types.ts +++ b/test/functional/apps/visualize/_chart_types.ts @@ -12,21 +12,17 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, getPageObjects }: FtrProviderContext) { - const deployment = getService('deployment'); const log = getService('log'); const PageObjects = getPageObjects(['visualize']); - let isOss = true; describe('chart types', function () { before(async function () { log.debug('navigateToApp visualize'); - isOss = await deployment.isOss(); await PageObjects.visualize.navigateToNewVisualization(); }); it('should show the promoted vis types for the first step', async function () { const expectedChartTypes = ['Custom visualization', 'Lens', 'Maps', 'TSVB']; - log.debug('oss= ' + isOss); // find all the chart types and make sure there all there const chartTypes = (await PageObjects.visualize.getPromotedVisTypes()).sort(); @@ -37,9 +33,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should show the correct agg based chart types', async function () { await PageObjects.visualize.clickAggBasedVisualizations(); - let expectedChartTypes = [ + const expectedChartTypes = [ 'Area', - 'Coordinate Map', 'Data table', 'Gauge', 'Goal', @@ -48,21 +43,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Line', 'Metric', 'Pie', - 'Region Map', 'Tag cloud', 'Timelion', 'Vertical bar', ]; - if (!isOss) { - expectedChartTypes = _.remove(expectedChartTypes, function (n) { - return n !== 'Coordinate Map'; - }); - expectedChartTypes = _.remove(expectedChartTypes, function (n) { - return n !== 'Region Map'; - }); - expectedChartTypes.sort(); - } - log.debug('oss= ' + isOss); // find all the chart types and make sure there all there const chartTypes = (await PageObjects.visualize.getChartTypes()).sort(); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 8dd2854419693..4170ada692e67 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -67,11 +67,15 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { this.tags('ciGroup9'); loadTestFile(require.resolve('./_embedding_chart')); - loadTestFile(require.resolve('./_chart_types')); loadTestFile(require.resolve('./_area_chart')); loadTestFile(require.resolve('./_data_table')); loadTestFile(require.resolve('./_data_table_nontimeindex')); loadTestFile(require.resolve('./_data_table_notimeindex_filters')); + + // this check is not needed when the CI doesn't run anymore for the OSS + if (!isOss) { + loadTestFile(require.resolve('./_chart_types')); + } }); describe('', function () { diff --git a/tsconfig.json b/tsconfig.json index b6742bffeab55..e7856aa0c8747 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,7 +26,6 @@ "src/plugins/kibana_react/**/*", "src/plugins/kibana_usage_collection/**/*", "src/plugins/kibana_utils/**/*", - "src/plugins/lens_oss/**/*", "src/plugins/management/**/*", "src/plugins/navigation/**/*", "src/plugins/newsfeed/**/*", @@ -81,7 +80,6 @@ { "path": "./src/plugins/kibana_react/tsconfig.json" }, { "path": "./src/plugins/kibana_usage_collection/tsconfig.json" }, { "path": "./src/plugins/kibana_utils/tsconfig.json" }, - { "path": "./src/plugins/lens_oss/tsconfig.json" }, { "path": "./src/plugins/management/tsconfig.json" }, { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 1bce19e2ee44a..5edfd4231a6d5 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -22,7 +22,6 @@ { "path": "./src/plugins/kibana_react/tsconfig.json" }, { "path": "./src/plugins/kibana_usage_collection/tsconfig.json" }, { "path": "./src/plugins/kibana_utils/tsconfig.json" }, - { "path": "./src/plugins/lens_oss/tsconfig.json" }, { "path": "./src/plugins/management/tsconfig.json" }, { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, diff --git a/x-pack/plugins/lens/kibana.json b/x-pack/plugins/lens/kibana.json index dc0a92ac702d0..9df3f41fbd855 100644 --- a/x-pack/plugins/lens/kibana.json +++ b/x-pack/plugins/lens/kibana.json @@ -19,5 +19,5 @@ "optionalPlugins": ["usageCollection", "taskManager", "globalSearch", "savedObjectsTagging"], "configPath": ["xpack", "lens"], "extraPublicDirs": ["common/constants"], - "requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable", "lensOss", "presentationUtil"] + "requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable", "presentationUtil"] } diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts index cdffdb342fd23..3fb7186aeac59 100644 --- a/x-pack/plugins/lens/public/plugin.ts +++ b/x-pack/plugins/lens/public/plugin.ts @@ -39,7 +39,6 @@ import { VISUALIZE_FIELD_TRIGGER, } from '../../../../src/plugins/ui_actions/public'; import { getEditPath, NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common'; -import { PLUGIN_ID_OSS } from '../../../../src/plugins/lens_oss/common/constants'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; import { visualizeFieldAction } from './trigger_actions/visualize_field_actions'; @@ -208,8 +207,6 @@ export class LensPlugin { start(core: CoreStart, startDependencies: LensPluginStartDependencies): LensPublicStart { const frameStart = this.editorFrameService.start(core, startDependencies); this.createEditorFrame = frameStart.createInstance; - // unregisters the OSS alias - startDependencies.visualizations.unRegisterAlias(PLUGIN_ID_OSS); // unregisters the Visualize action and registers the lens one if (startDependencies.uiActions.hasAction(ACTION_VISUALIZE_FIELD)) { startDependencies.uiActions.unregisterAction(ACTION_VISUALIZE_FIELD); diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 7ac5a2980d0ba..636d2f44b0217 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -35,7 +35,6 @@ { "path": "../../../src/plugins/kibana_utils/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/embeddable/tsconfig.json"}, - { "path": "../../../src/plugins/lens_oss/tsconfig.json"}, { "path": "../../../src/plugins/presentation_util/tsconfig.json"}, ] } \ No newline at end of file diff --git a/x-pack/plugins/maps/kibana.json b/x-pack/plugins/maps/kibana.json index 5f6f5224e32a3..2536601d0e6b1 100644 --- a/x-pack/plugins/maps/kibana.json +++ b/x-pack/plugins/maps/kibana.json @@ -23,5 +23,5 @@ "ui": true, "server": true, "extraPublicDirs": ["common/constants"], - "requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss", "presentationUtil"] + "requiredBundles": ["kibanaReact", "kibanaUtils", "home", "presentationUtil"] } diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index dd256126fae62..4173328a41d57 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -34,7 +34,6 @@ import { VisualizationsStart, } from '../../../../src/plugins/visualizations/public'; import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants'; -import { PLUGIN_ID_OSS } from '../../../../src/plugins/maps_oss/common/constants'; import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public'; import { createMapsUrlGenerator, @@ -162,9 +161,6 @@ export class MapsPlugin setLicensingPluginStart(plugins.licensing); setStartServices(core, plugins); - // unregisters the OSS alias - plugins.visualizations.unRegisterAlias(PLUGIN_ID_OSS); - if (core.application.capabilities.maps.show) { plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e28b41e0ce5e3..da9228335a3f3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3032,10 +3032,6 @@ "kibanaOverview.manageData.sectionTitle": "データを管理", "kibanaOverview.more.title": "Elasticではさまざまなことが可能です", "kibanaOverview.news.title": "新機能", - "lensOss.visTypeAlias.description": "ドラッグアンドドロップエディターでビジュアライゼーションを作成します。いつでもビジュアライゼーションタイプを切り替えることができます。ほとんどのビジュアライゼーションに最適です。", - "lensOss.visTypeAlias.note": "ほとんどのユーザーに推奨されます。", - "lensOss.visTypeAlias.promoTooltip.description": "Elastic では Lens を無料でお試しいただけます。詳細情報", - "lensOss.visTypeAlias.title": "レンズ", "management.breadcrumb": "スタック管理", "management.landing.header": "Stack Management {version}へようこそ", "management.landing.subhead": "インデックス、インデックスパターン、保存されたオブジェクト、Kibanaの設定、その他を管理します。", @@ -3089,9 +3085,6 @@ "maps_legacy.wmsOptions.wmsStylesLabel": "WMSスタイル", "maps_legacy.wmsOptions.wmsUrlLabel": "WMS URL", "maps_legacy.wmsOptions.wmsVersionLabel": "WMS バージョン", - "mapsOss.visTypeAlias.description": "マルチレイヤーマップで地理データをプロットしてスタイル設定できます。", - "mapsOss.visTypeAlias.promoTooltip.description": "Elastic では Maps を無料でお試しいただけます。詳細情報", - "mapsOss.visTypeAlias.title": "マップ", "monaco.painlessLanguage.autocomplete.docKeywordDescription": "doc['field_name'] 構文を使用して、スクリプトからフィールド値にアクセスします", "monaco.painlessLanguage.autocomplete.emitKeywordDescription": "戻らずに値を発行します。", "monaco.painlessLanguage.autocomplete.fieldValueDescription": "フィールド「{fieldName}」の値を取得します", @@ -4692,7 +4685,6 @@ "visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています", "visualizations.newVisWizard.aggBasedGroupDescription": "クラシック Visualize ライブラリを使用して、アグリゲーションに基づいてグラフを作成します。", "visualizations.newVisWizard.aggBasedGroupTitle": "アグリゲーションに基づく", - "visualizations.newVisWizard.basicTitle": "基本", "visualizations.newVisWizard.chooseSourceTitle": "ソースの選択", "visualizations.newVisWizard.experimentalTitle": "実験的", "visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 49fd9ce095f66..679edaf9e0cdd 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3036,10 +3036,6 @@ "kibanaOverview.manageData.sectionTitle": "管理您的数据", "kibanaOverview.more.title": "Elastic 让您事半功倍", "kibanaOverview.news.title": "最新动态", - "lensOss.visTypeAlias.description": "使用我们支持拖放的编辑器来创建可视化。随时在可视化类型之间切换。绝大多数可视化的最佳选择。", - "lensOss.visTypeAlias.note": "适合绝大多数用户。", - "lensOss.visTypeAlias.promoTooltip.description": "免费试用 Elastic 的 Lens。了解详情。", - "lensOss.visTypeAlias.title": "Lens", "management.breadcrumb": "Stack Management", "management.landing.header": "欢迎使用 Stack Management {version}", "management.landing.subhead": "管理您的索引、索引模式、已保存对象、Kibana 设置等等。", @@ -3093,9 +3089,6 @@ "maps_legacy.wmsOptions.wmsStylesLabel": "WMS 样式", "maps_legacy.wmsOptions.wmsUrlLabel": "WMS url", "maps_legacy.wmsOptions.wmsVersionLabel": "WMS 版本", - "mapsOss.visTypeAlias.description": "在多层地图中绘制地理数据并设置其样式。", - "mapsOss.visTypeAlias.promoTooltip.description": "免费试用 Elastic 的地图。了解详情。", - "mapsOss.visTypeAlias.title": "地图", "monaco.painlessLanguage.autocomplete.docKeywordDescription": "使用 doc['field_name'] 语法,从脚本中访问字段值", "monaco.painlessLanguage.autocomplete.emitKeywordDescription": "发出值,而不返回值。", "monaco.painlessLanguage.autocomplete.fieldValueDescription": "检索字段“{fieldName}”的值", @@ -4697,7 +4690,6 @@ "visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合", "visualizations.newVisWizard.aggBasedGroupDescription": "使用我们的经典可视化库,基于聚合创建图表。", "visualizations.newVisWizard.aggBasedGroupTitle": "基于聚合", - "visualizations.newVisWizard.basicTitle": "基本级", "visualizations.newVisWizard.chooseSourceTitle": "选择源", "visualizations.newVisWizard.experimentalTitle": "实验性", "visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。", From b8947e3e1574d69cc91b297743908b48daf6acba Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 27 Jan 2021 11:52:13 +0100 Subject: [PATCH 77/90] [Search Sessions] Make search session indicator UI opt-in, refactor per-app capabilities (#88699) --- .../kibana-plugin-plugins-data-public.md | 1 + ...nosearchsessionstoragecapabilitymessage.md | 13 ++++ .../public/application/dashboard_router.tsx | 1 + .../embeddable/dashboard_container.tsx | 3 +- .../hooks/use_dashboard_state_manager.ts | 15 ++++- .../dashboard/public/application/types.ts | 1 + src/plugins/data/public/index.ts | 1 + src/plugins/data/public/public.api.md | 35 ++++++----- src/plugins/data/public/search/index.ts | 1 + .../data/public/search/session/i18n.ts | 20 +++++++ .../data/public/search/session/index.ts | 1 + .../data/public/search/session/mocks.ts | 4 +- .../search/session/session_service.test.ts | 60 ++++++++++++++++++- .../public/search/session/session_service.ts | 59 ++++++++++++++---- .../public/application/angular/discover.js | 14 ++++- ...onnected_search_session_indicator.test.tsx | 37 +++++++++--- .../connected_search_session_indicator.tsx | 29 +++------ .../config.ts | 1 + .../services/index.ts | 4 +- ...nd_to_background.ts => search_sessions.ts} | 26 +++++--- .../async_search/sessions_in_space.ts | 57 +++++++++++++++++- .../tests/apps/discover/sessions_in_space.ts | 59 +++++++++++++++++- .../tests/apps/lens/index.ts | 22 +++++++ .../tests/apps/lens/search_sessions.ts | 35 +++++++++++ 24 files changed, 424 insertions(+), 75 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md create mode 100644 src/plugins/data/public/search/session/i18n.ts rename x-pack/test/send_search_to_background_integration/services/{send_to_background.ts => search_sessions.ts} (81%) create mode 100644 x-pack/test/send_search_to_background_integration/tests/apps/lens/index.ts create mode 100644 x-pack/test/send_search_to_background_integration/tests/apps/lens/search_sessions.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index 65a722868b37f..4bbc76b78ba03 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -125,6 +125,7 @@ | [isPartialResponse](./kibana-plugin-plugins-data-public.ispartialresponse.md) | | | [isQuery](./kibana-plugin-plugins-data-public.isquery.md) | | | [isTimeRange](./kibana-plugin-plugins-data-public.istimerange.md) | | +| [noSearchSessionStorageCapabilityMessage](./kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md) | Message to display in case storing session session is disabled due to turned off capability | | [parseSearchSourceJSON](./kibana-plugin-plugins-data-public.parsesearchsourcejson.md) | | | [QueryStringInput](./kibana-plugin-plugins-data-public.querystringinput.md) | | | [search](./kibana-plugin-plugins-data-public.search.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md new file mode 100644 index 0000000000000..2bb0a0db8f9b3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [noSearchSessionStorageCapabilityMessage](./kibana-plugin-plugins-data-public.nosearchsessionstoragecapabilitymessage.md) + +## noSearchSessionStorageCapabilityMessage variable + +Message to display in case storing session session is disabled due to turned off capability + +Signature: + +```typescript +noSearchSessionStorageCapabilityMessage: string +``` diff --git a/src/plugins/dashboard/public/application/dashboard_router.tsx b/src/plugins/dashboard/public/application/dashboard_router.tsx index 9141f2e592fd7..5206c76f50be2 100644 --- a/src/plugins/dashboard/public/application/dashboard_router.tsx +++ b/src/plugins/dashboard/public/application/dashboard_router.tsx @@ -104,6 +104,7 @@ export async function mountApp({ mapsCapabilities: { save: Boolean(coreStart.application.capabilities.maps?.save) }, createShortUrl: Boolean(coreStart.application.capabilities.dashboard.createShortUrl), visualizeCapabilities: { save: Boolean(coreStart.application.capabilities.visualize?.save) }, + storeSearchSession: Boolean(coreStart.application.capabilities.dashboard.storeSearchSession), }, }; diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx index fa520ec22497b..780eb1bad8c2b 100644 --- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx +++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx @@ -89,7 +89,7 @@ export interface InheritedChildInput extends IndexSignature { export type DashboardReactContextValue = KibanaReactContextValue; export type DashboardReactContext = KibanaReactContext; -const defaultCapabilities = { +const defaultCapabilities: DashboardCapabilities = { show: false, createNew: false, saveQuery: false, @@ -97,6 +97,7 @@ const defaultCapabilities = { hideWriteControls: true, mapsCapabilities: { save: false }, visualizeCapabilities: { save: false }, + storeSearchSession: true, }; export class DashboardContainer extends Container { diff --git a/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts b/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts index a4044e8668e59..93fbb50950850 100644 --- a/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts +++ b/src/plugins/dashboard/public/application/hooks/use_dashboard_state_manager.ts @@ -16,6 +16,7 @@ import { useKibana } from '../../services/kibana_react'; import { connectToQueryState, esFilters, + noSearchSessionStorageCapabilityMessage, QueryState, syncQueryStateWithUrl, } from '../../services/data'; @@ -159,13 +160,22 @@ export const useDashboardStateManager = ( stateManager.isNew() ); - searchSession.setSearchSessionInfoProvider( + searchSession.enableStorage( createSessionRestorationDataProvider({ data: dataPlugin, getDashboardTitle: () => dashboardTitle, getDashboardId: () => savedDashboard?.id || '', getAppState: () => stateManager.getAppState(), - }) + }), + { + isDisabled: () => + dashboardCapabilities.storeSearchSession + ? { disabled: false } + : { + disabled: true, + reasonText: noSearchSessionStorageCapabilityMessage, + }, + } ); setDashboardStateManager(stateManager); @@ -192,6 +202,7 @@ export const useDashboardStateManager = ( toasts, uiSettings, usageCollection, + dashboardCapabilities.storeSearchSession, ]); return { dashboardStateManager, viewMode, setViewMode }; diff --git a/src/plugins/dashboard/public/application/types.ts b/src/plugins/dashboard/public/application/types.ts index 61e16beed61f4..e4f9388a919d1 100644 --- a/src/plugins/dashboard/public/application/types.ts +++ b/src/plugins/dashboard/public/application/types.ts @@ -55,6 +55,7 @@ export interface DashboardCapabilities { saveQuery: boolean; createNew: boolean; show: boolean; + storeSearchSession: boolean; } export interface DashboardAppServices { diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index ff3e2ebc89a41..fc8c44e8d1870 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -384,6 +384,7 @@ export { SearchTimeoutError, TimeoutErrorMode, PainlessError, + noSearchSessionStorageCapabilityMessage, } from './search'; export type { diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 002f033365790..9e493f46b0781 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1834,6 +1834,11 @@ export enum METRIC_TYPES { TOP_HITS = "top_hits" } +// Warning: (ae-missing-release-tag) "noSearchSessionStorageCapabilityMessage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const noSearchSessionStorageCapabilityMessage: string; + // Warning: (ae-missing-release-tag) "OptionedParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -2629,21 +2634,21 @@ export const UI_SETTINGS: { // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "validateIndexPattern" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:418:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:422:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:426:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:414:1 - (ae-forgotten-export) The symbol "Ipv4Address" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:419:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:420:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:423:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:424:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:427:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:34:5 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/search/session/session_service.ts:41:5 - (ae-forgotten-export) The symbol "UrlGeneratorStateMapping" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 1deffc9c2d55e..3d87411883a67 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -37,6 +37,7 @@ export { SearchSessionState, SessionsClient, ISessionsClient, + noSearchSessionStorageCapabilityMessage, } from './session'; export { getEsPreference } from './es_search'; diff --git a/src/plugins/data/public/search/session/i18n.ts b/src/plugins/data/public/search/session/i18n.ts new file mode 100644 index 0000000000000..2ee36b46dfd5a --- /dev/null +++ b/src/plugins/data/public/search/session/i18n.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; + +/** + * Message to display in case storing + * session session is disabled due to turned off capability + */ +export const noSearchSessionStorageCapabilityMessage = i18n.translate( + 'data.searchSessionIndicator.noCapability', + { + defaultMessage: "You don't have permissions to create search sessions.", + } +); diff --git a/src/plugins/data/public/search/session/index.ts b/src/plugins/data/public/search/session/index.ts index ab311d56fe096..f3f0c34c1be75 100644 --- a/src/plugins/data/public/search/session/index.ts +++ b/src/plugins/data/public/search/session/index.ts @@ -9,3 +9,4 @@ export { SessionService, ISessionService, SearchSessionInfoProvider } from './session_service'; export { SearchSessionState } from './search_session_state'; export { SessionsClient, ISessionsClient } from './sessions_client'; +export { noSearchSessionStorageCapabilityMessage } from './i18n'; diff --git a/src/plugins/data/public/search/session/mocks.ts b/src/plugins/data/public/search/session/mocks.ts index 6a7a207b90d46..679898e3e51dd 100644 --- a/src/plugins/data/public/search/session/mocks.ts +++ b/src/plugins/data/public/search/session/mocks.ts @@ -29,7 +29,6 @@ export function getSessionServiceMock(): jest.Mocked { getSessionId: jest.fn(), getSession$: jest.fn(() => new BehaviorSubject(undefined).asObservable()), state$: new BehaviorSubject(SearchSessionState.None).asObservable(), - setSearchSessionInfoProvider: jest.fn(), trackSearch: jest.fn((searchDescriptor) => () => {}), destroy: jest.fn(), onRefresh$: new Subject(), @@ -40,5 +39,8 @@ export function getSessionServiceMock(): jest.Mocked { save: jest.fn(), isCurrentSession: jest.fn(), getSearchOptions: jest.fn(), + enableStorage: jest.fn(), + isSessionStorageReady: jest.fn(() => true), + getSearchSessionIndicatorUiConfig: jest.fn(() => ({ isDisabled: () => ({ disabled: false }) })), }; } diff --git a/src/plugins/data/public/search/session/session_service.test.ts b/src/plugins/data/public/search/session/session_service.test.ts index 7797d92c4e07d..21c38c68e6a83 100644 --- a/src/plugins/data/public/search/session/session_service.test.ts +++ b/src/plugins/data/public/search/session/session_service.test.ts @@ -113,7 +113,7 @@ describe('Session service', () => { sessionId, }); - sessionService.setSearchSessionInfoProvider({ + sessionService.enableStorage({ getName: async () => 'Name', getUrlGeneratorData: async () => ({ urlGeneratorId: 'id', @@ -156,4 +156,62 @@ describe('Session service', () => { expect(sessionService.isCurrentSession('some-other')).toBeFalsy(); expect(sessionService.isCurrentSession(sessionId)).toBeTruthy(); }); + + test('enableStorage() enables storage capabilities', async () => { + sessionService.start(); + await expect(() => sessionService.save()).rejects.toThrowErrorMatchingInlineSnapshot( + `"No info provider for current session"` + ); + + expect(sessionService.isSessionStorageReady()).toBe(false); + + sessionService.enableStorage({ + getName: async () => 'Name', + getUrlGeneratorData: async () => ({ + urlGeneratorId: 'id', + initialState: {}, + restoreState: {}, + }), + }); + + expect(sessionService.isSessionStorageReady()).toBe(true); + + await expect(() => sessionService.save()).resolves; + + sessionService.clear(); + expect(sessionService.isSessionStorageReady()).toBe(false); + }); + + test('can provide config for search session indicator', () => { + expect(sessionService.getSearchSessionIndicatorUiConfig().isDisabled().disabled).toBe(false); + sessionService.enableStorage( + { + getName: async () => 'Name', + getUrlGeneratorData: async () => ({ + urlGeneratorId: 'id', + initialState: {}, + restoreState: {}, + }), + }, + { + isDisabled: () => ({ disabled: true, reasonText: 'text' }), + } + ); + + expect(sessionService.getSearchSessionIndicatorUiConfig().isDisabled().disabled).toBe(true); + + sessionService.clear(); + expect(sessionService.getSearchSessionIndicatorUiConfig().isDisabled().disabled).toBe(false); + }); + + test('save() throws in case getUrlGeneratorData returns throws', async () => { + sessionService.enableStorage({ + getName: async () => 'Name', + getUrlGeneratorData: async () => { + throw new Error('Haha'); + }, + }); + sessionService.start(); + await expect(() => sessionService.save()).rejects.toMatchInlineSnapshot(`[Error: Haha]`); + }); }); diff --git a/src/plugins/data/public/search/session/session_service.ts b/src/plugins/data/public/search/session/session_service.ts index 6269398036e76..23129a9afc460 100644 --- a/src/plugins/data/public/search/session/session_service.ts +++ b/src/plugins/data/public/search/session/session_service.ts @@ -43,6 +43,20 @@ export interface SearchSessionInfoProvider; } +/** + * Configure a "Search session indicator" UI + */ +export interface SearchSessionIndicatorUiConfig { + /** + * App controls if "Search session indicator" UI should be disabled. + * reasonText will appear in a tooltip. + * + * Could be used, for example, to disable "Search session indicator" UI + * in case user doesn't have permissions to store a search session + */ + isDisabled: () => { disabled: true; reasonText: string } | { disabled: false }; +} + /** * Responsible for tracking a current search session. Supports only a single session at a time. */ @@ -51,6 +65,7 @@ export class SessionService { private readonly state: SessionStateContainer; private searchSessionInfoProvider?: SearchSessionInfoProvider; + private searchSessionIndicatorUiConfig?: Partial; private subscription = new Subscription(); private curApp?: string; @@ -102,17 +117,6 @@ export class SessionService { }); } - /** - * Set a provider of info about current session - * This will be used for creating a search session saved object - * @param searchSessionInfoProvider - */ - public setSearchSessionInfoProvider( - searchSessionInfoProvider: SearchSessionInfoProvider | undefined - ) { - this.searchSessionInfoProvider = searchSessionInfoProvider; - } - /** * Used to track pending searches within current session * @@ -185,7 +189,8 @@ export class SessionService { */ public clear() { this.state.transitions.clear(); - this.setSearchSessionInfoProvider(undefined); + this.searchSessionInfoProvider = undefined; + this.searchSessionIndicatorUiConfig = undefined; } private refresh$ = new Subject(); @@ -269,4 +274,34 @@ export class SessionService { isStored: isCurrentSession ? this.isStored() : false, }; } + + /** + * Provide an info about current session which is needed for storing a search session. + * To opt-into "Search session indicator" UI app has to call {@link enableStorage}. + * + * @param searchSessionInfoProvider - info provider for saving a search session + * @param searchSessionIndicatorUiConfig - config for "Search session indicator" UI + */ + public enableStorage( + searchSessionInfoProvider: SearchSessionInfoProvider, + searchSessionIndicatorUiConfig?: SearchSessionIndicatorUiConfig + ) { + this.searchSessionInfoProvider = searchSessionInfoProvider; + this.searchSessionIndicatorUiConfig = searchSessionIndicatorUiConfig; + } + + /** + * If the current app explicitly called {@link enableStorage} and provided all configuration needed + * for storing its search sessions + */ + public isSessionStorageReady(): boolean { + return !!this.searchSessionInfoProvider; + } + + public getSearchSessionIndicatorUiConfig(): SearchSessionIndicatorUiConfig { + return { + isDisabled: () => ({ disabled: false }), + ...this.searchSessionIndicatorUiConfig, + }; + } } diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 946baa7f4ecb1..5c26680c7cc45 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -18,6 +18,7 @@ import { connectToQueryState, esFilters, indexPatterns as indexPatternsUtils, + noSearchSessionStorageCapabilityMessage, syncQueryStateWithUrl, } from '../../../../data/public'; import { getSortArray } from './doc_table'; @@ -284,12 +285,21 @@ function discoverController($route, $scope, Promise) { } }); - data.search.session.setSearchSessionInfoProvider( + data.search.session.enableStorage( createSearchSessionRestorationDataProvider({ appStateContainer, data, getSavedSearch: () => savedSearch, - }) + }), + { + isDisabled: () => + capabilities.discover.storeSearchSession + ? { disabled: false } + : { + disabled: true, + reasonText: noSearchSessionStorageCapabilityMessage, + }, + } ); $scope.setIndexPattern = async (id) => { diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx index 2c74f9c995a5a..f4bb7577bee53 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.test.tsx @@ -28,6 +28,12 @@ timeFilter.getRefreshInterval.mockImplementation(() => refreshInterval$.getValue beforeEach(() => { refreshInterval$.next({ value: 0, pause: true }); + sessionService.isSessionStorageReady.mockImplementation(() => true); + sessionService.getSearchSessionIndicatorUiConfig.mockImplementation(() => ({ + isDisabled: () => ({ + disabled: false, + }), + })); }); test("shouldn't show indicator in case no active search session", async () => { @@ -45,6 +51,22 @@ test("shouldn't show indicator in case no active search session", async () => { expect(container).toMatchInlineSnapshot(`
`); }); +test("shouldn't show indicator in case app hasn't opt-in", async () => { + const SearchSessionIndicator = createConnectedSearchSessionIndicator({ + sessionService, + application: coreStart.application, + timeFilter, + }); + const { getByTestId, container } = render(); + sessionService.isSessionStorageReady.mockImplementation(() => false); + + // make sure `searchSessionIndicator` isn't appearing after some time (lazy-loading) + await expect( + waitFor(() => getByTestId('searchSessionIndicator'), { timeout: 100 }) + ).rejects.toThrow(); + expect(container).toMatchInlineSnapshot(`
`); +}); + test('should show indicator in case there is an active search session', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ @@ -57,7 +79,7 @@ test('should show indicator in case there is an active search session', async () await waitFor(() => getByTestId('searchSessionIndicator')); }); -test('should be disabled when permissions are off', async () => { +test('should be disabled in case uiConfig says so ', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); coreStart.application.currentAppId$ = new BehaviorSubject('discover'); (coreStart.application.capabilities as any) = { @@ -65,6 +87,12 @@ test('should be disabled when permissions are off', async () => { storeSearchSession: false, }, }; + sessionService.getSearchSessionIndicatorUiConfig.mockImplementation(() => ({ + isDisabled: () => ({ + disabled: true, + reasonText: 'reason', + }), + })); const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, application: coreStart.application, @@ -80,12 +108,7 @@ test('should be disabled when permissions are off', async () => { test('should be disabled during auto-refresh', async () => { const state$ = new BehaviorSubject(SearchSessionState.Loading); - coreStart.application.currentAppId$ = new BehaviorSubject('discover'); - (coreStart.application.capabilities as any) = { - discover: { - storeSearchSession: true, - }, - }; + const SearchSessionIndicator = createConnectedSearchSessionIndicator({ sessionService: { ...sessionService, state$ }, application: coreStart.application, diff --git a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx index 5c8c01064bff4..59c1bb4a223b1 100644 --- a/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx +++ b/x-pack/plugins/data_enhanced/public/search/ui/connected_search_session_indicator/connected_search_session_indicator.tsx @@ -29,35 +29,14 @@ export const createConnectedSearchSessionIndicator = ({ .getRefreshIntervalUpdate$() .pipe(map(isAutoRefreshEnabled), distinctUntilChanged()); - const getCapabilitiesByAppId = ( - capabilities: ApplicationStart['capabilities'], - appId?: string - ) => { - switch (appId) { - case 'dashboards': - return capabilities.dashboard; - case 'discover': - return capabilities.discover; - default: - return undefined; - } - }; - return () => { const state = useObservable(sessionService.state$.pipe(debounceTime(500))); const autoRefreshEnabled = useObservable(isAutoRefreshEnabled$, isAutoRefreshEnabled()); - const appId = useObservable(application.currentAppId$, undefined); + const isDisabledByApp = sessionService.getSearchSessionIndicatorUiConfig().isDisabled(); let disabled = false; let disabledReasonText: string = ''; - if (getCapabilitiesByAppId(application.capabilities, appId)?.storeSearchSession !== true) { - disabled = true; - disabledReasonText = i18n.translate('xpack.data.searchSessionIndicator.noCapability', { - defaultMessage: "You don't have permissions to send to background.", - }); - } - if (autoRefreshEnabled) { disabled = true; disabledReasonText = i18n.translate( @@ -68,6 +47,12 @@ export const createConnectedSearchSessionIndicator = ({ ); } + if (isDisabledByApp.disabled) { + disabled = true; + disabledReasonText = isDisabledByApp.reasonText; + } + + if (!sessionService.isSessionStorageReady()) return null; if (!state) return null; return ( diff --git a/x-pack/test/send_search_to_background_integration/config.ts b/x-pack/test/send_search_to_background_integration/config.ts index bad818bb69664..c788cc38477e6 100644 --- a/x-pack/test/send_search_to_background_integration/config.ts +++ b/x-pack/test/send_search_to_background_integration/config.ts @@ -24,6 +24,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { resolve(__dirname, './tests/apps/dashboard/async_search'), resolve(__dirname, './tests/apps/discover'), resolve(__dirname, './tests/apps/management/search_sessions'), + resolve(__dirname, './tests/apps/lens'), ], kbnTestServer: { diff --git a/x-pack/test/send_search_to_background_integration/services/index.ts b/x-pack/test/send_search_to_background_integration/services/index.ts index 35eed5a218b42..b177ac3a01cb0 100644 --- a/x-pack/test/send_search_to_background_integration/services/index.ts +++ b/x-pack/test/send_search_to_background_integration/services/index.ts @@ -5,9 +5,9 @@ */ import { services as functionalServices } from '../../functional/services'; -import { SendToBackgroundProvider } from './send_to_background'; +import { SearchSessionsProvider } from './search_sessions'; export const services = { ...functionalServices, - searchSessions: SendToBackgroundProvider, + searchSessions: SearchSessionsProvider, }; diff --git a/x-pack/test/send_search_to_background_integration/services/send_to_background.ts b/x-pack/test/send_search_to_background_integration/services/search_sessions.ts similarity index 81% rename from x-pack/test/send_search_to_background_integration/services/send_to_background.ts rename to x-pack/test/send_search_to_background_integration/services/search_sessions.ts index 8c3261c2074ae..7041de6462243 100644 --- a/x-pack/test/send_search_to_background_integration/services/send_to_background.ts +++ b/x-pack/test/send_search_to_background_integration/services/search_sessions.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { SavedObjectsFindResponse } from 'src/core/server'; import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -20,7 +21,7 @@ type SessionStateType = | 'restored' | 'canceled'; -export function SendToBackgroundProvider({ getService }: FtrProviderContext) { +export function SearchSessionsProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const log = getService('log'); const retry = getService('retry'); @@ -36,6 +37,17 @@ export function SendToBackgroundProvider({ getService }: FtrProviderContext) { return testSubjects.exists(SEARCH_SESSION_INDICATOR_TEST_SUBJ); } + public async missingOrFail(): Promise { + return testSubjects.missingOrFail(SEARCH_SESSION_INDICATOR_TEST_SUBJ); + } + + public async disabledOrFail() { + await this.exists(); + await expect(await (await (await this.find()).findByTagName('button')).isEnabled()).to.be( + false + ); + } + public async expectState(state: SessionStateType) { return retry.waitFor(`searchSessions indicator to get into state = ${state}`, async () => { const currentState = await ( @@ -93,12 +105,12 @@ export function SendToBackgroundProvider({ getService }: FtrProviderContext) { } /* - * This cleanup function should be used by tests that create new background sesions. - * Tests should not end with new background sessions remaining in storage since that interferes with functional tests that check the _find API. - * Alternatively, a test can navigate to `Managment > Search Sessions` and use the UI to delete any created tests. + * This cleanup function should be used by tests that create new search sessions. + * Tests should not end with new search sessions remaining in storage since that interferes with functional tests that check the _find API. + * Alternatively, a test can navigate to `Management > Search Sessions` and use the UI to delete any created tests. */ public async deleteAllSearchSessions() { - log.debug('Deleting created background sessions'); + log.debug('Deleting created searcg sessions'); // ignores 409 errs and keeps retrying await retry.tryForTime(10000, async () => { const { body } = await supertest @@ -109,10 +121,10 @@ export function SendToBackgroundProvider({ getService }: FtrProviderContext) { .expect(200); const { saved_objects: savedObjects } = body as SavedObjectsFindResponse; - log.debug(`Found created background sessions: ${savedObjects.map(({ id }) => id)}`); + log.debug(`Found created search sessions: ${savedObjects.map(({ id }) => id)}`); await Promise.all( savedObjects.map(async (so) => { - log.debug(`Deleting background session: ${so.id}`); + log.debug(`Deleting search session: ${so.id}`); await supertest .delete(`/internal/session/${so.id}`) .set(`kbn-xsrf`, `anything`) diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts index f590e44138642..6aea4368a2f3f 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/dashboard/async_search/sessions_in_space.ts @@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const searchSessions = getService('searchSessions'); describe('dashboard in space', () => { - describe('Send to background in space', () => { + describe('Storing search sessions in space', () => { before(async () => { await esArchiver.load('dashboard/session_in_space'); @@ -92,5 +92,60 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.missingOrFail('embeddableErrorLabel'); }); }); + + describe('Disabled storing search sessions', () => { + before(async () => { + await esArchiver.load('dashboard/session_in_space'); + + await security.role.create('data_analyst', { + elasticsearch: { + indices: [{ names: ['logstash-*'], privileges: ['all'] }], + }, + kibana: [ + { + feature: { + dashboard: ['minimal_read'], + }, + spaces: ['another-space'], + }, + ], + }); + + await security.user.create('analyst', { + password: 'analyst-password', + roles: ['data_analyst'], + full_name: 'test user', + }); + + await PageObjects.security.forceLogout(); + + await PageObjects.security.login('analyst', 'analyst-password', { + expectSpaceSelector: false, + }); + }); + + after(async () => { + await security.role.delete('data_analyst'); + await security.user.delete('analyst'); + + await esArchiver.unload('dashboard/session_in_space'); + await PageObjects.security.forceLogout(); + }); + + it("Doesn't allow to store a session", async () => { + await PageObjects.common.navigateToApp('dashboard', { basePath: 's/another-space' }); + await PageObjects.dashboard.loadSavedDashboard('A Dashboard in another space'); + + await PageObjects.timePicker.setAbsoluteRange( + 'Sep 1, 2015 @ 00:00:00.000', + 'Oct 1, 2015 @ 00:00:00.000' + ); + + await PageObjects.dashboard.waitForRenderComplete(); + + await searchSessions.expectState('completed'); + await searchSessions.disabledOrFail(); + }); + }); }); } diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts index 6384afb179593..733b2edd4cd06 100644 --- a/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts +++ b/x-pack/test/send_search_to_background_integration/tests/apps/discover/sessions_in_space.ts @@ -23,7 +23,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const searchSessions = getService('searchSessions'); describe('discover in space', () => { - describe('Send to background in space', () => { + describe('Storing search sessions in space', () => { before(async () => { await esArchiver.load('dashboard/session_in_space'); @@ -93,7 +93,62 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // Check that session is restored await searchSessions.expectState('restored'); - await testSubjects.missingOrFail('embeddableErrorLabel'); + await testSubjects.missingOrFail('discoverNoResultsError'); // expect error because of fake searchSessionId + }); + }); + describe('Disabled storing search sessions in space', () => { + before(async () => { + await esArchiver.load('dashboard/session_in_space'); + + await security.role.create('data_analyst', { + elasticsearch: { + indices: [{ names: ['logstash-*'], privileges: ['all'] }], + }, + kibana: [ + { + feature: { + discover: ['read'], + }, + spaces: ['another-space'], + }, + ], + }); + + await security.user.create('analyst', { + password: 'analyst-password', + roles: ['data_analyst'], + full_name: 'test user', + }); + + await PageObjects.security.forceLogout(); + + await PageObjects.security.login('analyst', 'analyst-password', { + expectSpaceSelector: false, + }); + }); + + after(async () => { + await security.role.delete('data_analyst'); + await security.user.delete('analyst'); + + await esArchiver.unload('dashboard/session_in_space'); + await PageObjects.security.forceLogout(); + }); + + it("Doesn't allow to store a session", async () => { + await PageObjects.common.navigateToApp('discover', { basePath: 's/another-space' }); + + await PageObjects.discover.selectIndexPattern('logstash-*'); + + await PageObjects.timePicker.setAbsoluteRange( + 'Sep 1, 2015 @ 00:00:00.000', + 'Oct 1, 2015 @ 00:00:00.000' + ); + + await PageObjects.discover.waitForDocTableLoadingComplete(); + + await searchSessions.expectState('completed'); + await searchSessions.disabledOrFail(); }); }); }); diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/lens/index.ts b/x-pack/test/send_search_to_background_integration/tests/apps/lens/index.ts new file mode 100644 index 0000000000000..e84cede4bc87d --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/lens/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ loadTestFile, getService }: FtrProviderContext) { + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('lens search sessions', function () { + this.tags('ciGroup3'); + + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*' }); + }); + + loadTestFile(require.resolve('./search_sessions.ts')); + }); +} diff --git a/x-pack/test/send_search_to_background_integration/tests/apps/lens/search_sessions.ts b/x-pack/test/send_search_to_background_integration/tests/apps/lens/search_sessions.ts new file mode 100644 index 0000000000000..6bd79f0f98883 --- /dev/null +++ b/x-pack/test/send_search_to_background_integration/tests/apps/lens/search_sessions.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * 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 ({ getPageObjects, getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const searchSession = getService('searchSessions'); + const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'timePicker', 'header']); + const listingTable = getService('listingTable'); + + describe('lens search sessions', () => { + before(async () => { + await esArchiver.loadIfNeeded('logstash_functional'); + await esArchiver.loadIfNeeded('lens/basic'); + }); + after(async () => { + await esArchiver.unload('lens/basic'); + }); + + it("doesn't shows search sessions indicator UI", async () => { + await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('lnsXYvis'); + await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.header.waitUntilLoadingHasFinished(); + expect(await PageObjects.lens.isShowingNoResults()).to.be(false); + + await searchSession.missingOrFail(); + }); + }); +} From c8afae8a5120850565ae3fa7f9db460a3f7be653 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Wed, 27 Jan 2021 12:13:15 +0100 Subject: [PATCH 78/90] Enable v2 so migrations, disable in FTR tests (#89297) * Enable v2 so migrations, disable in FTR tests * Disable v2 migrations for ui_settings integration tests * Disable v2 migrations for reporting without serucity api integration test --- src/core/server/saved_objects/saved_objects_config.ts | 2 +- src/core/server/ui_settings/integration_tests/lib/servers.ts | 3 +++ test/common/config.js | 2 ++ .../reporting_without_security.config.ts | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/server/saved_objects/saved_objects_config.ts b/src/core/server/saved_objects/saved_objects_config.ts index 82db64daf2bd3..0885a52078f5c 100644 --- a/src/core/server/saved_objects/saved_objects_config.ts +++ b/src/core/server/saved_objects/saved_objects_config.ts @@ -18,7 +18,7 @@ export const savedObjectsMigrationConfig = { pollInterval: schema.number({ defaultValue: 1500 }), skip: schema.boolean({ defaultValue: false }), // TODO migrationsV2: remove/deprecate once we release migrations v2 - enableV2: schema.boolean({ defaultValue: false }), + enableV2: schema.boolean({ defaultValue: true }), }), }; diff --git a/src/core/server/ui_settings/integration_tests/lib/servers.ts b/src/core/server/ui_settings/integration_tests/lib/servers.ts index f181272030ae1..b5198b19007d0 100644 --- a/src/core/server/ui_settings/integration_tests/lib/servers.ts +++ b/src/core/server/ui_settings/integration_tests/lib/servers.ts @@ -37,6 +37,9 @@ export async function startServers() { adjustTimeout: (t) => jest.setTimeout(t), settings: { kbn: { + migrations: { + enableV2: false, + }, uiSettings: { overrides: { foo: 'bar', diff --git a/test/common/config.js b/test/common/config.js index 8a42e6c87b214..b6d12444b7017 100644 --- a/test/common/config.js +++ b/test/common/config.js @@ -61,6 +61,8 @@ export default function () { ...(!!process.env.CODE_COVERAGE ? [`--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'coverage')}`] : []), + // Disable v2 migrations in tests for now + '--migrations.enableV2=false', ], }, services, diff --git a/x-pack/test/reporting_api_integration/reporting_without_security.config.ts b/x-pack/test/reporting_api_integration/reporting_without_security.config.ts index 4a95a15169b59..11182bbcdb3b0 100644 --- a/x-pack/test/reporting_api_integration/reporting_without_security.config.ts +++ b/x-pack/test/reporting_api_integration/reporting_without_security.config.ts @@ -32,6 +32,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { kbnTestServer: { ...apiConfig.get('kbnTestServer'), serverArgs: [ + `--migrations.enableV2=false`, `--elasticsearch.hosts=${formatUrl(esTestConfig.getUrlParts())}`, `--logging.json=false`, `--server.maxPayloadBytes=1679958`, From 9e6897505447d7a5911b568eb334e658bd95df78 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Wed, 27 Jan 2021 12:35:38 +0100 Subject: [PATCH 79/90] [APM] Upgrade ES client (#86594) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/apm/public/hooks/use_fetcher.tsx | 32 ++++---- .../scripts/upload-telemetry-data/index.ts | 12 +-- .../collect_data_telemetry/index.ts | 18 +++-- .../apm/server/lib/apm_telemetry/index.ts | 22 +++--- ...with_debug.ts => call_async_with_debug.ts} | 58 +++++++------- .../cancel_es_request_on_abort.ts | 27 +++++++ .../create_apm_event_client/index.test.ts | 77 +++++++++++++++++++ .../create_apm_event_client/index.ts | 45 +++++++---- .../create_internal_es_client/index.ts | 77 ++++++++++++------- .../server/lib/helpers/setup_request.test.ts | 56 +++++++------- .../apm/server/lib/helpers/setup_request.ts | 2 +- .../annotations/get_stored_annotations.ts | 17 ++-- .../server/lib/services/annotations/index.ts | 8 +- .../create_agent_config_index.ts | 8 +- .../custom_link/create_custom_link_index.ts | 8 +- x-pack/plugins/apm/server/plugin.ts | 6 +- .../apm/server/routes/create_api/index.ts | 7 +- x-pack/plugins/apm/server/routes/services.ts | 2 +- x-pack/plugins/observability/server/index.ts | 2 + .../lib/annotations/bootstrap_annotations.ts | 2 +- .../annotations/create_annotations_client.ts | 70 ++++++++++------- .../annotations/register_annotation_apis.ts | 4 +- .../server/utils/create_or_update_index.ts | 28 +++---- .../server/utils/unwrap_es_response.ts | 13 ++++ x-pack/typings/elasticsearch/index.d.ts | 5 +- 25 files changed, 396 insertions(+), 210 deletions(-) rename x-pack/plugins/apm/server/lib/helpers/create_es_client/{call_client_with_debug.ts => call_async_with_debug.ts} (51%) create mode 100644 x-pack/plugins/apm/server/lib/helpers/create_es_client/cancel_es_request_on_abort.ts create mode 100644 x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts create mode 100644 x-pack/plugins/observability/server/utils/unwrap_es_response.ts diff --git a/x-pack/plugins/apm/public/hooks/use_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_fetcher.tsx index 8174f06e06b8b..2b58f30a9ec64 100644 --- a/x-pack/plugins/apm/public/hooks/use_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_fetcher.tsx @@ -24,6 +24,21 @@ export interface FetcherResult { error?: IHttpFetchError; } +function getDetailsFromErrorResponse(error: IHttpFetchError) { + const message = error.body?.message ?? error.response?.statusText; + return ( + <> + {message} ({error.response?.status}) +
+ {i18n.translate('xpack.apm.fetcher.error.url', { + defaultMessage: `URL`, + })} +
+ {error.response?.url} + + ); +} + // fetcher functions can return undefined OR a promise. Previously we had a more simple type // but it led to issues when using object destructuring with default values type InferResponseType = Exclude extends Promise< @@ -82,25 +97,14 @@ export function useFetcher( if (!didCancel) { const errorDetails = - 'response' in err ? ( - <> - {err.response?.statusText} ({err.response?.status}) -
- {i18n.translate('xpack.apm.fetcher.error.url', { - defaultMessage: `URL`, - })} -
- {err.response?.url} - - ) : ( - err.message - ); + 'response' in err ? getDetailsFromErrorResponse(err) : err.message; if (showToastOnError) { - notifications.toasts.addWarning({ + notifications.toasts.addDanger({ title: i18n.translate('xpack.apm.fetcher.error.title', { defaultMessage: `Error while fetching resource`, }), + text: toMountPoint(
diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts index 8c64c37d9b7f7..e3221c17f3f2a 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts @@ -17,6 +17,8 @@ import { argv } from 'yargs'; import { Logger } from 'kibana/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { CollectTelemetryParams } from '../../server/lib/apm_telemetry/collect_data_telemetry'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { unwrapEsResponse } from '../../../observability/server/utils/unwrap_es_response'; import { downloadTelemetryTemplate } from '../shared/download-telemetry-template'; import { mergeApmTelemetryMapping } from '../../common/apm_telemetry'; import { generateSampleDocuments } from './generate-sample-documents'; @@ -80,18 +82,18 @@ async function uploadData() { apmAgentConfigurationIndex: '.apm-agent-configuration', }, search: (body) => { - return client.search(body as any).then((res) => res.body as any); + return unwrapEsResponse(client.search(body)); }, indicesStats: (body) => { - return client.indices.stats(body as any).then((res) => res.body); + return unwrapEsResponse(client.indices.stats(body)); }, transportRequest: ((params) => { - return client.transport - .request({ + return unwrapEsResponse( + client.transport.request({ method: params.method, path: params.path, }) - .then((res) => res.body); + ); }) as CollectTelemetryParams['transportRequest'], }, }); diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts index 730645c609cb6..90aad48fe20b9 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/collect_data_telemetry/index.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import { merge } from 'lodash'; -import { Logger, LegacyCallAPIOptions } from 'kibana/server'; -import { IndicesStatsParams, Client } from 'elasticsearch'; +import { Logger } from 'kibana/server'; +import { RequestParams } from '@elastic/elasticsearch'; import { ESSearchRequest, ESSearchResponse, @@ -20,9 +20,17 @@ type TelemetryTaskExecutor = (params: { params: TSearchRequest ): Promise>; indicesStats( - params: IndicesStatsParams, - options?: LegacyCallAPIOptions - ): ReturnType; + params: RequestParams.IndicesStats + // promise returned by client has an abort property + // so we cannot use its ReturnType + ): Promise<{ + _all?: { + total?: { store?: { size_in_bytes?: number }; docs?: { count?: number } }; + }; + _shards?: { + total?: number; + }; + }>; transportRequest: (params: { path: string; method: 'get'; diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts index 6d91e64be034d..98abff08dab5e 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts @@ -11,6 +11,7 @@ import { Logger, SavedObjectsErrorHelpers, } from '../../../../../../src/core/server'; +import { unwrapEsResponse } from '../../../../observability/server'; import { APMConfig } from '../..'; import { TaskManagerSetupContract, @@ -65,27 +66,22 @@ export async function createApmTelemetry({ const collectAndStore = async () => { const config = await config$.pipe(take(1)).toPromise(); const [{ elasticsearch }] = await core.getStartServices(); - const esClient = elasticsearch.legacy.client; + const esClient = elasticsearch.client; const indices = await getApmIndices({ config, savedObjectsClient, }); - const search = esClient.callAsInternalUser.bind( - esClient, - 'search' - ) as CollectTelemetryParams['search']; + const search: CollectTelemetryParams['search'] = (params) => + unwrapEsResponse(esClient.asInternalUser.search(params)); - const indicesStats = esClient.callAsInternalUser.bind( - esClient, - 'indices.stats' - ) as CollectTelemetryParams['indicesStats']; + const indicesStats: CollectTelemetryParams['indicesStats'] = (params) => + unwrapEsResponse(esClient.asInternalUser.indices.stats(params)); - const transportRequest = esClient.callAsInternalUser.bind( - esClient, - 'transport.request' - ) as CollectTelemetryParams['transportRequest']; + const transportRequest: CollectTelemetryParams['transportRequest'] = ( + params + ) => unwrapEsResponse(esClient.asInternalUser.transport.request(params)); const dataTelemetry = await collectDataTelemetry({ search, diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/call_client_with_debug.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/call_async_with_debug.ts similarity index 51% rename from x-pack/plugins/apm/server/lib/helpers/create_es_client/call_client_with_debug.ts rename to x-pack/plugins/apm/server/lib/helpers/create_es_client/call_async_with_debug.ts index 9f7aaafbefb8c..9d612d82d99bb 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/call_client_with_debug.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/call_async_with_debug.ts @@ -7,34 +7,31 @@ /* eslint-disable no-console */ import chalk from 'chalk'; -import { - LegacyAPICaller, - KibanaRequest, -} from '../../../../../../../src/core/server'; +import { KibanaRequest } from '../../../../../../../src/core/server'; function formatObj(obj: Record) { return JSON.stringify(obj, null, 2); } -export async function callClientWithDebug({ - apiCaller, - operationName, - params, +export async function callAsyncWithDebug({ + cb, + getDebugMessage, debug, - request, }: { - apiCaller: LegacyAPICaller; - operationName: string; - params: Record; + cb: () => Promise; + getDebugMessage: () => { body: string; title: string }; debug: boolean; - request: KibanaRequest; }) { + if (!debug) { + return cb(); + } + const startTime = process.hrtime(); let res: any; let esError = null; try { - res = await apiCaller(operationName, params); + res = await cb(); } catch (e) { // catch error and throw after outputting debug info esError = e; @@ -44,23 +41,14 @@ export async function callClientWithDebug({ const highlightColor = esError ? 'bgRed' : 'inverse'; const diff = process.hrtime(startTime); const duration = `${Math.round(diff[0] * 1000 + diff[1] / 1e6)}ms`; - const routeInfo = `${request.route.method.toUpperCase()} ${ - request.route.path - }`; + + const { title, body } = getDebugMessage(); console.log( - chalk.bold[highlightColor](`=== Debug: ${routeInfo} (${duration}) ===`) + chalk.bold[highlightColor](`=== Debug: ${title} (${duration}) ===`) ); - if (operationName === 'search') { - console.log(`GET ${params.index}/_${operationName}`); - console.log(formatObj(params.body)); - } else { - console.log(chalk.bold('ES operation:'), operationName); - - console.log(chalk.bold('ES query:')); - console.log(formatObj(params)); - } + console.log(body); console.log(`\n`); } @@ -70,3 +58,19 @@ export async function callClientWithDebug({ return res; } + +export const getDebugBody = ( + params: Record, + operationName: string +) => { + if (operationName === 'search') { + return `GET ${params.index}/_search\n${formatObj(params.body)}`; + } + + return `${chalk.bold('ES operation:')} ${operationName}\n${chalk.bold( + 'ES query:' + )}\n${formatObj(params)}`; +}; + +export const getDebugTitle = (request: KibanaRequest) => + `${request.route.method.toUpperCase()} ${request.route.path}`; diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/cancel_es_request_on_abort.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/cancel_es_request_on_abort.ts new file mode 100644 index 0000000000000..e9b61a27f4380 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/cancel_es_request_on_abort.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { KibanaRequest } from 'src/core/server'; + +export function cancelEsRequestOnAbort>( + promise: T, + request: KibanaRequest +) { + const subscription = request.events.aborted$.subscribe(() => { + promise.abort(); + }); + + // using .catch() here means unsubscribe will be called + // after it has thrown an error, so we use .then(onSuccess, onFailure) + // syntax + promise.then( + () => subscription.unsubscribe(), + () => subscription.unsubscribe() + ); + + return promise; +} diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts new file mode 100644 index 0000000000000..f58e04061254d --- /dev/null +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts @@ -0,0 +1,77 @@ +/* + * 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 { contextServiceMock } from 'src/core/server/mocks'; +import { createHttpServer } from 'src/core/server/test_utils'; +import supertest from 'supertest'; +import { createApmEventClient } from '.'; + +describe('createApmEventClient', () => { + let server: ReturnType; + + beforeEach(() => { + server = createHttpServer(); + }); + + afterEach(async () => { + await server.stop(); + }); + it('cancels a search when a request is aborted', async () => { + const { server: innerServer, createRouter } = await server.setup({ + context: contextServiceMock.createSetupContract(), + }); + const router = createRouter('/'); + + const abort = jest.fn(); + router.get( + { path: '/', validate: false }, + async (context, request, res) => { + const eventClient = createApmEventClient({ + esClient: { + search: () => { + return Object.assign( + new Promise((resolve) => setTimeout(resolve, 3000)), + { abort } + ); + }, + } as any, + debug: false, + request, + indices: {} as any, + options: { + includeFrozen: false, + }, + }); + + await eventClient.search({ + apm: { + events: [], + }, + }); + + return res.ok({ body: 'ok' }); + } + ); + + await server.start(); + + const incomingRequest = supertest(innerServer.listener) + .get('/') + // end required to send request + .end(); + + await new Promise((resolve) => { + setTimeout(() => { + incomingRequest.abort(); + setTimeout(() => { + resolve(undefined); + }, 0); + }, 50); + }); + + expect(abort).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts index b7c38068eb93e..b2e25994d6fe6 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.ts @@ -5,10 +5,11 @@ */ import { ValuesType } from 'utility-types'; +import { unwrapEsResponse } from '../../../../../../observability/server'; import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; import { + ElasticsearchClient, KibanaRequest, - LegacyScopedClusterClient, } from '../../../../../../../../src/core/server'; import { ProcessorEvent } from '../../../../../common/processor_event'; import { @@ -17,11 +18,16 @@ import { } from '../../../../../../../typings/elasticsearch'; import { ApmIndicesConfig } from '../../../settings/apm_indices/get_apm_indices'; import { addFilterToExcludeLegacyData } from './add_filter_to_exclude_legacy_data'; -import { callClientWithDebug } from '../call_client_with_debug'; import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { Span } from '../../../../../typings/es_schemas/ui/span'; import { Metric } from '../../../../../typings/es_schemas/ui/metric'; import { unpackProcessorEvents } from './unpack_processor_events'; +import { + callAsyncWithDebug, + getDebugTitle, + getDebugBody, +} from '../call_async_with_debug'; +import { cancelEsRequestOnAbort } from '../cancel_es_request_on_abort'; export type APMEventESSearchRequest = Omit & { apm: { @@ -59,10 +65,7 @@ export function createApmEventClient({ indices, options: { includeFrozen } = { includeFrozen: false }, }: { - esClient: Pick< - LegacyScopedClusterClient, - 'callAsInternalUser' | 'callAsCurrentUser' - >; + esClient: ElasticsearchClient; debug: boolean; request: KibanaRequest; indices: ApmIndicesConfig; @@ -71,9 +74,9 @@ export function createApmEventClient({ }; }) { return { - search( + async search( params: TParams, - { includeLegacyData } = { includeLegacyData: false } + { includeLegacyData = false } = {} ): Promise> { const withProcessorEventFilter = unpackProcessorEvents(params, indices); @@ -81,15 +84,25 @@ export function createApmEventClient({ ? addFilterToExcludeLegacyData(withProcessorEventFilter) : withProcessorEventFilter; - return callClientWithDebug({ - apiCaller: esClient.callAsCurrentUser, - operationName: 'search', - params: { - ...withPossibleLegacyDataFilter, - ignore_throttled: !includeFrozen, - ignore_unavailable: true, + const searchParams = { + ...withPossibleLegacyDataFilter, + ignore_throttled: !includeFrozen, + ignore_unavailable: true, + }; + + return callAsyncWithDebug({ + cb: () => { + const searchPromise = cancelEsRequestOnAbort( + esClient.search(searchParams), + request + ); + + return unwrapEsResponse(searchPromise); }, - request, + getDebugMessage: () => ({ + body: getDebugBody(searchParams, 'search'), + title: getDebugTitle(request), + }), debug, }); }, diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts index 8e74a7992e9ea..69f596520d216 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_internal_es_client/index.ts @@ -3,23 +3,23 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { - IndexDocumentParams, - IndicesCreateParams, - DeleteDocumentResponse, - DeleteDocumentParams, -} from 'elasticsearch'; import { KibanaRequest } from 'src/core/server'; +import { RequestParams } from '@elastic/elasticsearch'; +import { TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { unwrapEsResponse } from '../../../../../../observability/server'; import { APMRequestHandlerContext } from '../../../../routes/typings'; import { ESSearchResponse, ESSearchRequest, } from '../../../../../../../typings/elasticsearch'; -import { callClientWithDebug } from '../call_client_with_debug'; +import { + callAsyncWithDebug, + getDebugBody, + getDebugTitle, +} from '../call_async_with_debug'; +import { cancelEsRequestOnAbort } from '../cancel_es_request_on_abort'; -// `type` was deprecated in 7.0 -export type APMIndexDocumentParams = Omit, 'type'>; +export type APMIndexDocumentParams = RequestParams.Index; export type APMInternalClient = ReturnType; @@ -30,17 +30,26 @@ export function createInternalESClient({ context: APMRequestHandlerContext; request: KibanaRequest; }) { - const { callAsInternalUser } = context.core.elasticsearch.legacy.client; + const { asInternalUser } = context.core.elasticsearch.client; - const callEs = (operationName: string, params: Record) => { - return callClientWithDebug({ - apiCaller: callAsInternalUser, - operationName, - params, - request, + function callEs({ + cb, + operationName, + params, + }: { + operationName: string; + cb: () => TransportRequestPromise; + params: Record; + }) { + return callAsyncWithDebug({ + cb: () => unwrapEsResponse(cancelEsRequestOnAbort(cb(), request)), + getDebugMessage: () => ({ + title: getDebugTitle(request), + body: getDebugBody(params, operationName), + }), debug: context.params.query._debug, }); - }; + } return { search: async < @@ -49,18 +58,32 @@ export function createInternalESClient({ >( params: TSearchRequest ): Promise> => { - return callEs('search', params); + return callEs({ + operationName: 'search', + cb: () => asInternalUser.search(params), + params, + }); }, - index: (params: APMIndexDocumentParams) => { - return callEs('index', params); + index: (params: APMIndexDocumentParams) => { + return callEs({ + operationName: 'index', + cb: () => asInternalUser.index(params), + params, + }); }, - delete: ( - params: Omit - ): Promise => { - return callEs('delete', params); + delete: (params: RequestParams.Delete): Promise<{ result: string }> => { + return callEs({ + operationName: 'delete', + cb: () => asInternalUser.delete(params), + params, + }); }, - indicesCreate: (params: IndicesCreateParams) => { - return callEs('indices.create', params); + indicesCreate: (params: RequestParams.IndicesCreate) => { + return callEs({ + operationName: 'indices.create', + cb: () => asInternalUser.indices.create(params), + params, + }); }, }; } diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts index f2d291cd053bb..f00941d6e6800 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -31,6 +31,15 @@ jest.mock('../index_pattern/get_dynamic_index_pattern', () => ({ })); function getMockRequest() { + const esClientMock = { + asCurrentUser: { + search: jest.fn().mockResolvedValue({ body: {} }), + }, + asInternalUser: { + search: jest.fn().mockResolvedValue({ body: {} }), + }, + }; + const mockContext = ({ config: new Proxy( {}, @@ -45,12 +54,7 @@ function getMockRequest() { }, core: { elasticsearch: { - legacy: { - client: { - callAsCurrentUser: jest.fn(), - callAsInternalUser: jest.fn(), - }, - }, + client: esClientMock, }, uiSettings: { client: { @@ -69,12 +73,7 @@ function getMockRequest() { } as unknown) as APMRequestHandlerContext & { core: { elasticsearch: { - legacy: { - client: { - callAsCurrentUser: jest.Mock; - callAsInternalUser: jest.Mock; - }; - }; + client: typeof esClientMock; }; uiSettings: { client: { @@ -91,6 +90,11 @@ function getMockRequest() { const mockRequest = ({ url: '', + events: { + aborted$: { + subscribe: jest.fn(), + }, + }, } as unknown) as KibanaRequest; return { mockContext, mockRequest }; @@ -106,8 +110,8 @@ describe('setupRequest', () => { body: { foo: 'bar' }, }); expect( - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser - ).toHaveBeenCalledWith('search', { + mockContext.core.elasticsearch.client.asCurrentUser.search + ).toHaveBeenCalledWith({ index: ['apm-*'], body: { foo: 'bar', @@ -133,8 +137,8 @@ describe('setupRequest', () => { body: { foo: 'bar' }, } as any); expect( - mockContext.core.elasticsearch.legacy.client.callAsInternalUser - ).toHaveBeenCalledWith('search', { + mockContext.core.elasticsearch.client.asInternalUser.search + ).toHaveBeenCalledWith({ index: ['apm-*'], body: { foo: 'bar', @@ -154,8 +158,8 @@ describe('setupRequest', () => { body: { query: { bool: { filter: [{ term: 'someTerm' }] } } }, }); const params = - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock - .calls[0][1]; + mockContext.core.elasticsearch.client.asCurrentUser.search.mock + .calls[0][0]; expect(params.body).toEqual({ query: { bool: { @@ -184,8 +188,8 @@ describe('setupRequest', () => { } ); const params = - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock - .calls[0][1]; + mockContext.core.elasticsearch.client.asCurrentUser.search.mock + .calls[0][0]; expect(params.body).toEqual({ query: { bool: { @@ -214,8 +218,8 @@ describe('without a bool filter', () => { }, }); const params = - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock - .calls[0][1]; + mockContext.core.elasticsearch.client.asCurrentUser.search.mock + .calls[0][0]; expect(params.body).toEqual({ query: { bool: { @@ -245,8 +249,8 @@ describe('with includeFrozen=false', () => { }); const params = - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock - .calls[0][1]; + mockContext.core.elasticsearch.client.asCurrentUser.search.mock + .calls[0][0]; expect(params.ignore_throttled).toBe(true); }); }); @@ -265,8 +269,8 @@ describe('with includeFrozen=true', () => { }); const params = - mockContext.core.elasticsearch.legacy.client.callAsCurrentUser.mock - .calls[0][1]; + mockContext.core.elasticsearch.client.asCurrentUser.search.mock + .calls[0][0]; expect(params.ignore_throttled).toBe(false); }); }); diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index 47529de1042a1..947eb68e10093 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -86,7 +86,7 @@ export async function setupRequest( const coreSetupRequest = { indices, apmEventClient: createApmEventClient({ - esClient: context.core.elasticsearch.legacy.client, + esClient: context.core.elasticsearch.client.asCurrentUser, debug: context.params.query._debug, request, indices, diff --git a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts index 3903298415aed..55395f3a4ca4e 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/get_stored_annotations.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, Logger } from 'kibana/server'; +import { ElasticsearchClient, Logger } from 'kibana/server'; +import { unwrapEsResponse } from '../../../../../observability/server'; import { rangeFilter } from '../../../../common/utils/range_filter'; import { ESSearchResponse } from '../../../../../../typings/elasticsearch'; import { Annotation as ESAnnotation } from '../../../../../observability/common/annotations'; @@ -18,14 +19,14 @@ export async function getStoredAnnotations({ setup, serviceName, environment, - apiCaller, + client, annotationsClient, logger, }: { setup: Setup & SetupTimeRange; serviceName: string; environment?: string; - apiCaller: LegacyAPICaller; + client: ElasticsearchClient; annotationsClient: ScopedAnnotationsClient; logger: Logger; }): Promise { @@ -50,10 +51,12 @@ export async function getStoredAnnotations({ const response: ESSearchResponse< ESAnnotation, { body: typeof body } - > = (await apiCaller('search', { - index: annotationsClient.index, - body, - })) as any; + > = await unwrapEsResponse( + client.search({ + index: annotationsClient.index, + body, + }) + ); return response.hits.hits.map((hit) => { return { diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.ts index 9516ed3777297..304485822be28 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/index.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/index.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, Logger } from 'kibana/server'; +import { ElasticsearchClient, Logger } from 'kibana/server'; import { ScopedAnnotationsClient } from '../../../../../observability/server'; import { getDerivedServiceAnnotations } from './get_derived_service_annotations'; import { Setup, SetupTimeRange } from '../../helpers/setup_request'; @@ -15,7 +15,7 @@ export async function getServiceAnnotations({ serviceName, environment, annotationsClient, - apiCaller, + client, logger, }: { serviceName: string; @@ -23,7 +23,7 @@ export async function getServiceAnnotations({ setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; annotationsClient?: ScopedAnnotationsClient; - apiCaller: LegacyAPICaller; + client: ElasticsearchClient; logger: Logger; }) { // start fetching derived annotations (based on transactions), but don't wait on it @@ -41,7 +41,7 @@ export async function getServiceAnnotations({ serviceName, environment, annotationsClient, - apiCaller, + client, logger, }) : []; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts index 83117db1167b5..190c99d0002d8 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/create_agent_config_index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyClusterClient, Logger } from 'src/core/server'; +import { ElasticsearchClient, Logger } from 'src/core/server'; import { createOrUpdateIndex, MappingsDefinition, @@ -13,18 +13,18 @@ import { APMConfig } from '../../..'; import { getApmIndicesConfig } from '../apm_indices/get_apm_indices'; export async function createApmAgentConfigurationIndex({ - esClient, + client, config, logger, }: { - esClient: ILegacyClusterClient; + client: ElasticsearchClient; config: APMConfig; logger: Logger; }) { const index = getApmIndicesConfig(config).apmAgentConfigurationIndex; return createOrUpdateIndex({ index, - apiCaller: esClient.callAsInternalUser, + client, logger, mappings, }); diff --git a/x-pack/plugins/apm/server/lib/settings/custom_link/create_custom_link_index.ts b/x-pack/plugins/apm/server/lib/settings/custom_link/create_custom_link_index.ts index 2bfe0d620e4e8..aa9e7411d1014 100644 --- a/x-pack/plugins/apm/server/lib/settings/custom_link/create_custom_link_index.ts +++ b/x-pack/plugins/apm/server/lib/settings/custom_link/create_custom_link_index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyClusterClient, Logger } from 'src/core/server'; +import { ElasticsearchClient, Logger } from 'src/core/server'; import { createOrUpdateIndex, MappingsDefinition, @@ -13,18 +13,18 @@ import { APMConfig } from '../../..'; import { getApmIndicesConfig } from '../apm_indices/get_apm_indices'; export const createApmCustomLinkIndex = async ({ - esClient, + client, config, logger, }: { - esClient: ILegacyClusterClient; + client: ElasticsearchClient; config: APMConfig; logger: Logger; }) => { const index = getApmIndicesConfig(config).apmCustomLinkIndex; return createOrUpdateIndex({ index, - apiCaller: esClient.callAsInternalUser, + client, logger, mappings, }); diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 09b75137e12df..3e01523aa8e31 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -173,7 +173,7 @@ export class APMPlugin implements Plugin { context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), ]); - const esClient = context.core.elasticsearch.legacy.client; + const esClient = context.core.elasticsearch.client.asCurrentUser; return createApmEventClient({ debug: debug ?? false, @@ -195,13 +195,13 @@ export class APMPlugin implements Plugin { // create agent configuration index without blocking start lifecycle createApmAgentConfigurationIndex({ - esClient: core.elasticsearch.legacy.client, + client: core.elasticsearch.client.asInternalUser, config: this.currentConfig, logger: this.logger, }); // create custom action index without blocking start lifecycle createApmCustomLinkIndex({ - esClient: core.elasticsearch.legacy.client, + client: core.elasticsearch.client.asInternalUser, config: this.currentConfig, logger: this.logger, }); diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index cfb31670bd521..721badf7fc025 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -147,7 +147,7 @@ function convertBoomToKibanaResponse( error: Boom.Boom, response: KibanaResponseFactory ) { - const opts = { body: error.message }; + const opts = { body: { message: error.message } }; switch (error.output.statusCode) { case 404: return response.notFound(opts); @@ -159,9 +159,6 @@ function convertBoomToKibanaResponse( return response.forbidden(opts); default: - return response.custom({ - statusCode: error.output.statusCode, - ...opts, - }); + throw error; } } diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index bfc2ebf062ac3..ef74437f5f0e7 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -194,7 +194,7 @@ export const serviceAnnotationsRoute = createRoute({ serviceName, environment, annotationsClient, - apiCaller: context.core.elasticsearch.legacy.client.callAsCurrentUser, + client: context.core.elasticsearch.client.asCurrentUser, logger: context.logger, }); }, diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index 78550b781b411..e88541f69d6cf 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -9,6 +9,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { ObservabilityPlugin, ObservabilityPluginSetup } from './plugin'; import { createOrUpdateIndex, MappingsDefinition } from './utils/create_or_update_index'; import { ScopedAnnotationsClient } from './lib/annotations/bootstrap_annotations'; +import { unwrapEsResponse } from './utils/unwrap_es_response'; export const config = { schema: schema.object({ @@ -30,4 +31,5 @@ export { MappingsDefinition, ObservabilityPluginSetup, ScopedAnnotationsClient, + unwrapEsResponse, }; diff --git a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts index 6fcd780d5af29..90084611d7efc 100644 --- a/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts +++ b/x-pack/plugins/observability/server/lib/annotations/bootstrap_annotations.ts @@ -38,7 +38,7 @@ export async function bootstrapAnnotations({ index, core, context }: Params) { ) => { return createAnnotationsClient({ index, - apiCaller: requestContext.core.elasticsearch.legacy.client.callAsCurrentUser, + esClient: requestContext.core.elasticsearch.client.asCurrentUser, logger, license: requestContext.licensing?.license, }); diff --git a/x-pack/plugins/observability/server/lib/annotations/create_annotations_client.ts b/x-pack/plugins/observability/server/lib/annotations/create_annotations_client.ts index 41f45683d244c..76890cbd587e9 100644 --- a/x-pack/plugins/observability/server/lib/annotations/create_annotations_client.ts +++ b/x-pack/plugins/observability/server/lib/annotations/create_annotations_client.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, Logger } from 'kibana/server'; +import { ElasticsearchClient, Logger } from 'kibana/server'; import * as t from 'io-ts'; -import { Client } from 'elasticsearch'; import Boom from '@hapi/boom'; import { ILicense } from '../../../../licensing/server'; import { @@ -15,9 +14,9 @@ import { Annotation, getAnnotationByIdRt, } from '../../../common/annotations'; -import { PromiseReturnType } from '../../../typings/common'; import { createOrUpdateIndex } from '../../utils/create_or_update_index'; import { mappings } from './mappings'; +import { unwrapEsResponse } from '../../utils/unwrap_es_response'; type CreateParams = t.TypeOf; type DeleteParams = t.TypeOf; @@ -38,19 +37,25 @@ interface IndexDocumentResponse { result: string; } +interface GetResponse { + _id: string; + _index: string; + _source: Annotation; +} + export function createAnnotationsClient(params: { index: string; - apiCaller: LegacyAPICaller; + esClient: ElasticsearchClient; logger: Logger; license?: ILicense; }) { - const { index, apiCaller, logger, license } = params; + const { index, esClient, logger, license } = params; const initIndex = () => createOrUpdateIndex({ index, mappings, - apiCaller, + client: esClient, logger, }); @@ -71,9 +76,11 @@ export function createAnnotationsClient(params: { async ( createParams: CreateParams ): Promise<{ _id: string; _index: string; _source: Annotation }> => { - const indexExists = await apiCaller('indices.exists', { - index, - }); + const indexExists = await unwrapEsResponse( + esClient.indices.exists({ + index, + }) + ); if (!indexExists) { await initIndex(); @@ -86,35 +93,42 @@ export function createAnnotationsClient(params: { }, }; - const response = (await apiCaller('index', { - index, - body: annotation, - refresh: 'wait_for', - })) as IndexDocumentResponse; + const body = await unwrapEsResponse( + esClient.index({ + index, + body: annotation, + refresh: 'wait_for', + }) + ); - return apiCaller('get', { - index, - id: response._id, - }); + return ( + await esClient.get({ + index, + id: body._id, + }) + ).body; } ), getById: ensureGoldLicense(async (getByIdParams: GetByIdParams) => { const { id } = getByIdParams; - return apiCaller('get', { - id, - index, - }); + return unwrapEsResponse( + esClient.get({ + id, + index, + }) + ); }), delete: ensureGoldLicense(async (deleteParams: DeleteParams) => { const { id } = deleteParams; - const response = (await apiCaller('delete', { - index, - id, - refresh: 'wait_for', - })) as PromiseReturnType; - return response; + return unwrapEsResponse( + esClient.delete({ + index, + id, + refresh: 'wait_for', + }) + ); }), }; } diff --git a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts index 8f0b53b5a3df2..6ae80880d22b5 100644 --- a/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts +++ b/x-pack/plugins/observability/server/lib/annotations/register_annotation_apis.ts @@ -55,11 +55,11 @@ export function registerAnnotationAPIs({ }); } - const apiCaller = context.core.elasticsearch.legacy.client.callAsCurrentUser; + const esClient = context.core.elasticsearch.client.asCurrentUser; const client = createAnnotationsClient({ index, - apiCaller, + esClient, logger, license: context.licensing?.license, }); diff --git a/x-pack/plugins/observability/server/utils/create_or_update_index.ts b/x-pack/plugins/observability/server/utils/create_or_update_index.ts index 1898331451262..248488b4a5ebe 100644 --- a/x-pack/plugins/observability/server/utils/create_or_update_index.ts +++ b/x-pack/plugins/observability/server/utils/create_or_update_index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import pRetry from 'p-retry'; -import { Logger, LegacyAPICaller } from 'src/core/server'; +import { Logger, ElasticsearchClient } from 'src/core/server'; export interface MappingsObject { type: string; @@ -24,12 +24,12 @@ export interface MappingsDefinition { export async function createOrUpdateIndex({ index, mappings, - apiCaller, + client, logger, }: { index: string; mappings: MappingsDefinition; - apiCaller: LegacyAPICaller; + client: ElasticsearchClient; logger: Logger; }) { try { @@ -43,21 +43,21 @@ export async function createOrUpdateIndex({ */ await pRetry( async () => { - const indexExists = await apiCaller('indices.exists', { index }); + const indexExists = (await client.indices.exists({ index })).body; const result = indexExists ? await updateExistingIndex({ index, - apiCaller, + client, mappings, }) : await createNewIndex({ index, - apiCaller, + client, mappings, }); - if (!result.acknowledged) { - const resultError = result && result.error && JSON.stringify(result.error); + if (!result.body.acknowledged) { + const resultError = result && result.body.error && JSON.stringify(result.body.error); throw new Error(resultError); } }, @@ -75,14 +75,14 @@ export async function createOrUpdateIndex({ function createNewIndex({ index, - apiCaller, + client, mappings, }: { index: string; - apiCaller: LegacyAPICaller; + client: ElasticsearchClient; mappings: MappingsDefinition; }) { - return apiCaller('indices.create', { + return client.indices.create<{ acknowledged: boolean; error: any }>({ index, body: { // auto_expand_replicas: Allows cluster to not have replicas for this index @@ -94,14 +94,14 @@ function createNewIndex({ function updateExistingIndex({ index, - apiCaller, + client, mappings, }: { index: string; - apiCaller: LegacyAPICaller; + client: ElasticsearchClient; mappings: MappingsDefinition; }) { - return apiCaller('indices.putMapping', { + return client.indices.putMapping<{ acknowledged: boolean; error: any }>({ index, body: mappings, }); diff --git a/x-pack/plugins/observability/server/utils/unwrap_es_response.ts b/x-pack/plugins/observability/server/utils/unwrap_es_response.ts new file mode 100644 index 0000000000000..418ceeb64cc87 --- /dev/null +++ b/x-pack/plugins/observability/server/utils/unwrap_es_response.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PromiseValueType } from '../../../apm/typings/common'; + +export function unwrapEsResponse>( + responsePromise: T +): Promise['body']> { + return responsePromise.then((res) => res.body); +} diff --git a/x-pack/typings/elasticsearch/index.d.ts b/x-pack/typings/elasticsearch/index.d.ts index ff20ce39d6446..049e1e52c66d9 100644 --- a/x-pack/typings/elasticsearch/index.d.ts +++ b/x-pack/typings/elasticsearch/index.d.ts @@ -5,6 +5,7 @@ */ import { ValuesType } from 'utility-types'; import { Explanation, SearchParams, SearchResponse } from 'elasticsearch'; +import { RequestParams } from '@elastic/elasticsearch'; import { AggregationResponseMap, AggregationInputMap, SortOptions } from './aggregations'; export { AggregationInputMap, @@ -72,9 +73,7 @@ export interface ESSearchBody { _source?: ESSourceOptions; } -export type ESSearchRequest = Omit & { - body?: ESSearchBody; -}; +export type ESSearchRequest = RequestParams.Search; export interface ESSearchOptions { restTotalHitsAsInt: boolean; From ab1af57f0564e64907ac88f1a7a6cde4262b94a7 Mon Sep 17 00:00:00 2001 From: Silvia Mitter Date: Wed, 27 Jan 2021 13:34:11 +0100 Subject: [PATCH 80/90] update apm index pattern (#89395) --- src/plugins/apm_oss/server/tutorial/index_pattern.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/apm_oss/server/tutorial/index_pattern.json b/src/plugins/apm_oss/server/tutorial/index_pattern.json index 6eb040f2758af..93a2393b70fa4 100644 --- a/src/plugins/apm_oss/server/tutorial/index_pattern.json +++ b/src/plugins/apm_oss/server/tutorial/index_pattern.json @@ -1,7 +1,7 @@ { "attributes": { "fieldFormatMap": "{\"client.bytes\":{\"id\":\"bytes\"},\"client.nat.port\":{\"id\":\"string\"},\"client.port\":{\"id\":\"string\"},\"destination.bytes\":{\"id\":\"bytes\"},\"destination.nat.port\":{\"id\":\"string\"},\"destination.port\":{\"id\":\"string\"},\"event.duration\":{\"id\":\"duration\",\"params\":{\"inputFormat\":\"nanoseconds\",\"outputFormat\":\"asMilliseconds\",\"outputPrecision\":1}},\"event.sequence\":{\"id\":\"string\"},\"event.severity\":{\"id\":\"string\"},\"http.request.body.bytes\":{\"id\":\"bytes\"},\"http.request.bytes\":{\"id\":\"bytes\"},\"http.response.body.bytes\":{\"id\":\"bytes\"},\"http.response.bytes\":{\"id\":\"bytes\"},\"http.response.status_code\":{\"id\":\"string\"},\"log.syslog.facility.code\":{\"id\":\"string\"},\"log.syslog.priority\":{\"id\":\"string\"},\"network.bytes\":{\"id\":\"bytes\"},\"package.size\":{\"id\":\"string\"},\"process.parent.pgid\":{\"id\":\"string\"},\"process.parent.pid\":{\"id\":\"string\"},\"process.parent.ppid\":{\"id\":\"string\"},\"process.parent.thread.id\":{\"id\":\"string\"},\"process.pgid\":{\"id\":\"string\"},\"process.pid\":{\"id\":\"string\"},\"process.ppid\":{\"id\":\"string\"},\"process.thread.id\":{\"id\":\"string\"},\"server.bytes\":{\"id\":\"bytes\"},\"server.nat.port\":{\"id\":\"string\"},\"server.port\":{\"id\":\"string\"},\"source.bytes\":{\"id\":\"bytes\"},\"source.nat.port\":{\"id\":\"string\"},\"source.port\":{\"id\":\"string\"},\"system.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.memory.actual.free\":{\"id\":\"bytes\"},\"system.memory.total\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.limit.bytes\":{\"id\":\"bytes\"},\"system.process.cgroup.memory.mem.usage.bytes\":{\"id\":\"bytes\"},\"system.process.cpu.total.norm.pct\":{\"id\":\"percent\"},\"system.process.memory.rss.bytes\":{\"id\":\"bytes\"},\"system.process.memory.size\":{\"id\":\"bytes\"},\"url.port\":{\"id\":\"string\"},\"view spans\":{\"id\":\"url\",\"params\":{\"labelTemplate\":\"View Spans\"}}}", - "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.changes.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.effective.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.email\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.target.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", + "fields": "[{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"@timestamp\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.build.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"client.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.account.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.availability_zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.instance.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.machine.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.project.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.region\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.image.tag\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"container.runtime\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"destination.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dll.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.data\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.ttl\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.answers.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.header_flags\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.op_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.class\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.question.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.resolved_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.response_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"dns.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"ecs.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"error.stack_trace.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.dataset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.end\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.ingested\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.kind\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"event.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.outcome\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.provider\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reason\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.risk_score_norm\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.sequence\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.timezone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"event.url\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.accessed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.attributes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.created\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.ctime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.device\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.drive_letter\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.gid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.group\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.inode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mode\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.mtime\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.owner\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.target_path.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"file.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"file.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.method\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.request.referrer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.body.content.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.mime_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.status_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.file.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.logger\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.file.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.origin.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"log.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.facility.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.priority\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"log.syslog.severity.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.application\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.community_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.direction\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.forwarded_ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.iana_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.inner.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.transport\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"network.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.egress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.alias\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.interface.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ingress.zone\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.build_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.checksum\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.install_scope\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.installed\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"package.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.args_count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.exists\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.status\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.subject_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.trusted\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.code_signature.valid\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.command_line.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.entity_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.executable.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.exit_code\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.hash.sha512\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.parent.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.architecture\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.company\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.file_version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.imphash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.original_file_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pe.product\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pgid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.pid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.ppid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.start\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.id\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.thread.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.title.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.uptime\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"process.working_directory.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.strings\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.data.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.hive\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"registry.value\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.hosts\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"related.user\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.author\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.license\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.ruleset\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.uuid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"rule.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"server.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.ephemeral_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.state\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.address\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.number\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.as.organization.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.city_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.continent_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.country_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.location\",\"scripted\":false,\"searchable\":true,\"type\":\"geo_point\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_iso_code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.geo.region_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.mac\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.ip\",\"scripted\":false,\"searchable\":true,\"type\":\"ip\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.nat.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.packets\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"source.user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.framework\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.tactic.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"threat.technique.subtechnique.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.cipher\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.ja3\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.server_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.supported_ciphers\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.client.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.client.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.established\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.next_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.resumed\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.certificate_chain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.md5\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha1\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.hash.sha256\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.issuer\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.ja3s\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.subject\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"tls.server.x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.server.x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"tls.version_protocol\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"trace.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.extension\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.fragment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.password\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.path\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.port\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.query\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.registered_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.scheme\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.subdomain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.top_level_domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"url.username\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.email\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.full_name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.domain\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.group.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.hash\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user.roles\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.device.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.original.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.family\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.full.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.kernel\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.platform\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.os.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"user_agent.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vlan.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.category\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.classification\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.description.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.enumeration\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.reference\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.report_id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.scanner.vendor\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.base\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.environmental\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.temporal\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.score.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"vulnerability.severity\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.alternative_names\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.issuer.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_after\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.not_before\",\"scripted\":false,\"searchable\":true,\"type\":\"date\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_curve\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":false,\"name\":\"x509.public_key_exponent\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.public_key_size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.serial_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.signature_algorithm\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.common_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.country\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.distinguished_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.locality\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organization\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.organizational_unit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.subject.state_or_province\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"x509.version_number\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"agent.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"fields\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"timeseries.instance\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"cloud.image.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"docker.container.labels\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.containerized\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.build\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"host.os.codename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.pod.uid\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.namespace\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.node.hostname\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.labels.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.annotations.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.replicaset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.deployment.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.statefulset.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"kubernetes.container.image\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"processor.event\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"timestamp.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.request.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"http.response.finished\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"enabled\":false,\"indexed\":false,\"name\":\"http.response.headers\",\"scripted\":false,\"searchable\":false},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.environment\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.language.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.runtime.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"service.framework.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.sampled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.name.text\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.breakdown.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"parent.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.listening\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"observer.version_major\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"experimental\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.culprit\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.grouping_key\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.code\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.module\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":4,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.exception.handled\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.level\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.logger_name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":2,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"error.log.param_message\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.root\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.subtype\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.self_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.total\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.memory.actual.free\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cpu.total.norm.pct\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.size\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.memory.rss.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.limit.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"system.process.cgroup.memory.mem.usage.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.duration\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.cpu.ns\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.samples.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.alloc_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_objects.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.inuse_space.bytes\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.top.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.function\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.filename\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"profile.stack.line\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.service.version\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"sourcemap.bundle_filepath\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"view spans\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"child.id\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.action\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.start.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":1,\"doc_values\":true,\"indexed\":true,\"name\":\"span.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.sync\",\"scripted\":false,\"searchable\":true,\"type\":\"boolean\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.link\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.db.rows_affected\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.resource\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.result\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.marks.*.*\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.cls\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.fid\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.tbt\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.sum\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.experience.longtask.max\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.span_count.dropped\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.queue.name\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.message.age.ms\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"transaction.duration.histogram\",\"scripted\":false,\"searchable\":true},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"metricset.period\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.count\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":true,\"indexed\":true,\"name\":\"span.destination.service.response_time.sum.us\",\"scripted\":false,\"searchable\":true,\"type\":\"number\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_id\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":true,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_type\",\"scripted\":false,\"searchable\":true,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_index\",\"scripted\":false,\"searchable\":false,\"type\":\"string\"},{\"aggregatable\":false,\"analyzed\":false,\"count\":0,\"doc_values\":false,\"indexed\":false,\"name\":\"_score\",\"scripted\":false,\"searchable\":false,\"type\":\"number\"}]", "sourceFilters": "[{\"value\":\"sourcemap.sourcemap\"}]", "timeFieldName": "@timestamp" }, From b09e010b16970239a61dc18363d3bc9c0e6db146 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 27 Jan 2021 13:43:55 +0100 Subject: [PATCH 81/90] [Lens] Clean up usage collector (#89109) --- x-pack/plugins/lens/server/usage/collectors.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/server/usage/collectors.ts b/x-pack/plugins/lens/server/usage/collectors.ts index c32fc0371ed8a..71a699aadb865 100644 --- a/x-pack/plugins/lens/server/usage/collectors.ts +++ b/x-pack/plugins/lens/server/usage/collectors.ts @@ -16,11 +16,6 @@ export function registerLensUsageCollector( usageCollection: UsageCollectionSetup, taskManager: Promise ) { - let isCollectorReady = false; - taskManager.then(() => { - // mark lensUsageCollector as ready to collect when the TaskManager is ready - isCollectorReady = true; - }); const lensUsageCollector = usageCollection.makeUsageCollector({ type: 'lens', async fetch() { @@ -55,7 +50,10 @@ export function registerLensUsageCollector( }; } }, - isReady: () => isCollectorReady, + isReady: async () => { + await taskManager; + return true; + }, schema: lensUsageSchema, }); From ba1e795b3f4341547d3acc4cf9df79646e2b412d Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 27 Jan 2021 14:23:47 +0100 Subject: [PATCH 82/90] [Lens] Fix indexpattern checks for missing references (#88840) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../dimension_panel/reference_editor.test.tsx | 76 +++++++++++++++- .../dimension_panel/reference_editor.tsx | 42 ++++++--- .../indexpattern_datasource/indexpattern.tsx | 11 ++- .../public/indexpattern_datasource/mocks.ts | 78 +++++++++++++++- .../operations/definitions/index.ts | 4 +- .../operations/layer_helpers.test.ts | 89 +++++++++++++------ .../operations/layer_helpers.ts | 30 ++++--- .../public/indexpattern_datasource/utils.ts | 26 +++++- 8 files changed, 294 insertions(+), 62 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx index 0891dd27fcf17..ed1b695640922 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/reference_editor.test.tsx @@ -13,7 +13,7 @@ import type { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'k import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import type { DataPublicPluginStart } from 'src/plugins/data/public'; import { OperationMetadata } from '../../types'; -import { createMockedIndexPattern } from '../mocks'; +import { createMockedIndexPattern, createMockedIndexPatternWithoutType } from '../mocks'; import { ReferenceEditor, ReferenceEditorProps } from './reference_editor'; import { insertOrReplaceColumn } from '../operations'; import { FieldSelect } from './field_select'; @@ -260,6 +260,48 @@ describe('reference editor', () => { ); }); + it("should show the sub-function as invalid if there's no field compatible with it", () => { + // This may happen for saved objects after changing the type of a field + wrapper = mount( + true, + }} + /> + ); + + const subFunctionSelect = wrapper + .find('[data-test-subj="indexPattern-reference-function"]') + .first(); + + expect(subFunctionSelect.prop('isInvalid')).toEqual(true); + expect(subFunctionSelect.prop('selectedOptions')).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + 'data-test-subj': 'lns-indexPatternDimension-avg incompatible', + label: 'Average', + value: 'avg', + }), + ]) + ); + }); + it('should hide the function selector when using a field-only selection style', () => { wrapper = mount( { expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(true); }); + it('should show the FieldSelect as invalid if the selected field is missing', () => { + wrapper = mount( + true, + }} + /> + ); + + const fieldSelect = wrapper.find(FieldSelect); + expect(fieldSelect.prop('fieldIsInvalid')).toEqual(true); + expect(fieldSelect.prop('selectedField')).toEqual('missing'); + expect(fieldSelect.prop('selectedOperationType')).toEqual('avg'); + expect(fieldSelect.prop('incompleteOperation')).toBeUndefined(); + expect(fieldSelect.prop('markAllFieldsCompatible')).toEqual(false); + }); + it('should show the ParamEditor for functions that offer one', () => { wrapper = mount( value === incompleteInfo.operationType)!] + const selectedOption = incompleteOperation + ? [functionOptions.find(({ value }) => value === incompleteOperation)!] : column ? [functionOptions.find(({ value }) => value === column.operationType)!] : []; // If the operationType is incomplete, the user needs to select a field- so // the function is marked as valid. - const showOperationInvalid = !column && !Boolean(incompleteInfo?.operationType); + const showOperationInvalid = !column && !Boolean(incompleteOperation); // The field is invalid if the operation has been updated without a field, // or if we are in a field-only mode but empty state - const showFieldInvalid = - Boolean(incompleteInfo?.operationType) || (selectionStyle === 'field' && !column); + const showFieldInvalid = Boolean(incompleteOperation) || (selectionStyle === 'field' && !column); + // Check if the field still exists to protect from changes + const showFieldMissingInvalid = !currentIndexPattern.getFieldByName( + incompleteField ?? (column as FieldBasedIndexPatternColumn)?.sourceField + ); + + // what about a field changing type and becoming invalid? + // Let's say this change makes the indexpattern without any number field but the operation was set to a numeric operation. + // At this point the ComboBox will crash. + // Therefore check if the selectedOption is in functionOptions and in case fill it in as disabled option + const showSelectionFunctionInvalid = Boolean(selectedOption.length && selectedOption[0] == null); + if (showSelectionFunctionInvalid) { + const selectedOperationType = incompleteOperation || column.operationType; + const brokenFunctionOption = { + label: operationPanels[selectedOperationType].displayName, + value: selectedOperationType, + className: 'lnsIndexPatternDimensionEditor__operation', + 'data-test-subj': `lns-indexPatternDimension-${selectedOperationType} incompatible`, + }; + functionOptions.push(brokenFunctionOption); + selectedOption[0] = brokenFunctionOption; + } return (
@@ -216,7 +236,7 @@ export function ReferenceEditor(props: ReferenceEditorProps) { defaultMessage: 'Choose a sub-function', })} fullWidth - isInvalid={showOperationInvalid} + isInvalid={showOperationInvalid || showSelectionFunctionInvalid} > { @@ -258,11 +278,11 @@ export function ReferenceEditor(props: ReferenceEditorProps) { defaultMessage: 'Select a field', })} fullWidth - isInvalid={showFieldInvalid} + isInvalid={showFieldInvalid || showFieldMissingInvalid} labelAppend={labelAppend} > - (getErrorMessages(layer) ?? []).map((message) => ({ - shortMessage: '', // Not displayed currently - longMessage: message, - })) + (getErrorMessages(layer, state.indexPatterns[layer.indexPatternId]) ?? []).map( + (message) => ({ + shortMessage: '', // Not displayed currently + longMessage: message, + }) + ) ); // Single layer case, no need to explain more diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts index d0cbcee61db6f..4aea9e8ac67a9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts @@ -6,7 +6,83 @@ import { DragContextState } from '../drag_drop'; import { getFieldByNameFactory } from './pure_helpers'; -import type { IndexPattern } from './types'; +import type { IndexPattern, IndexPatternField } from './types'; + +export const createMockedIndexPatternWithoutType = ( + typeToFilter: IndexPatternField['type'] +): IndexPattern => { + const fields = [ + { + name: 'timestamp', + displayName: 'timestampLabel', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'start_date', + displayName: 'start_date', + type: 'date', + aggregatable: true, + searchable: true, + }, + { + name: 'bytes', + displayName: 'bytes', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'memory', + displayName: 'memory', + type: 'number', + aggregatable: true, + searchable: true, + }, + { + name: 'source', + displayName: 'source', + type: 'string', + aggregatable: true, + searchable: true, + esTypes: ['keyword'], + }, + { + name: 'unsupported', + displayName: 'unsupported', + type: 'geo', + aggregatable: true, + searchable: true, + }, + { + name: 'dest', + displayName: 'dest', + type: 'string', + aggregatable: true, + searchable: true, + esTypes: ['keyword'], + }, + { + name: 'scripted', + displayName: 'Scripted', + type: 'string', + searchable: true, + aggregatable: true, + scripted: true, + lang: 'painless', + script: '1234', + }, + ].filter(({ type }) => type !== typeToFilter); + return { + id: '1', + title: 'my-fake-index-pattern', + timeFieldName: 'timestamp', + hasRestrictions: false, + fields, + getFieldByName: getFieldByNameFactory(fields), + }; +}; export const createMockedIndexPattern = (): IndexPattern => { const fields = [ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts index 7dbc7d3b986a5..1cdaff53c5458 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts @@ -197,7 +197,7 @@ interface BaseOperationDefinitionProps { getErrorMessage?: ( layer: IndexPatternLayer, columnId: string, - indexPattern?: IndexPattern + indexPattern: IndexPattern ) => string[] | undefined; /* @@ -301,7 +301,7 @@ interface FieldBasedOperationDefinition { getErrorMessage: ( layer: IndexPatternLayer, columnId: string, - indexPattern?: IndexPattern + indexPattern: IndexPattern ) => string[] | undefined; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts index c0e71dc1509e7..94cf13a5c50a4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.test.ts @@ -2147,14 +2147,17 @@ describe('state_helpers', () => { it('should collect errors from metric-type operation definitions', () => { const mock = jest.fn().mockReturnValue(['error 1']); operationDefinitionMap.avg.getErrorMessage = mock; - const errors = getErrorMessages({ - indexPatternId: '1', - columnOrder: [], - columns: { - // @ts-expect-error invalid column - col1: { operationType: 'avg' }, + const errors = getErrorMessages( + { + indexPatternId: '1', + columnOrder: [], + columns: { + // @ts-expect-error invalid column + col1: { operationType: 'avg' }, + }, }, - }); + indexPattern + ); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); @@ -2162,15 +2165,18 @@ describe('state_helpers', () => { it('should collect errors from reference-type operation definitions', () => { const mock = jest.fn().mockReturnValue(['error 1']); operationDefinitionMap.testReference.getErrorMessage = mock; - const errors = getErrorMessages({ - indexPatternId: '1', - columnOrder: [], - columns: { - col1: - // @ts-expect-error not statically analyzed - { operationType: 'testReference', references: [] }, + const errors = getErrorMessages( + { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: + // @ts-expect-error not statically analyzed + { operationType: 'testReference', references: [] }, + }, }, - }); + indexPattern + ); expect(mock).toHaveBeenCalled(); expect(errors).toHaveLength(1); }); @@ -2184,24 +2190,55 @@ describe('state_helpers', () => { getErrorMessage: incompleteRef, }; - const errors = getErrorMessages({ - indexPatternId: '1', - columnOrder: [], - columns: { - col1: + const errors = getErrorMessages( + { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: + // @ts-expect-error not statically analyzed + { operationType: 'testReference', references: [] }, + }, + incompleteColumns: { // @ts-expect-error not statically analyzed - { operationType: 'testReference', references: [] }, - }, - incompleteColumns: { - // @ts-expect-error not statically analyzed - col1: { operationType: 'testIncompleteReference' }, + col1: { operationType: 'testIncompleteReference' }, + }, }, - }); + indexPattern + ); expect(savedRef).not.toHaveBeenCalled(); expect(incompleteRef).toHaveBeenCalled(); expect(errors).toBeUndefined(); delete operationDefinitionMap.testIncompleteReference; }); + + it('should forward the indexpattern when available', () => { + const mock = jest.fn(); + operationDefinitionMap.testReference.getErrorMessage = mock; + getErrorMessages( + { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: + // @ts-expect-error not statically analyzed + { operationType: 'testReference', references: [] }, + }, + }, + indexPattern + ); + expect(mock).toHaveBeenCalledWith( + { + indexPatternId: '1', + columnOrder: [], + columns: { + col1: { operationType: 'testReference', references: [] }, + }, + }, + 'col1', + indexPattern + ); + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts index d8244f3902a6e..10618cc754556 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/layer_helpers.ts @@ -864,18 +864,24 @@ export function updateLayerIndexPattern( * - All column references are valid * - All prerequisites are met */ -export function getErrorMessages(layer: IndexPatternLayer): string[] | undefined { - const errors: string[] = []; - Object.entries(layer.columns).forEach(([columnId, column]) => { - // If we're transitioning to another operation, check for "new" incompleteColumns rather - // than "old" saved operation on the layer - const columnFinalRef = - layer.incompleteColumns?.[columnId]?.operationType || column.operationType; - const def = operationDefinitionMap[columnFinalRef]; - if (def.getErrorMessage) { - errors.push(...(def.getErrorMessage(layer, columnId) ?? [])); - } - }); +export function getErrorMessages( + layer: IndexPatternLayer, + indexPattern: IndexPattern +): string[] | undefined { + const errors: string[] = Object.entries(layer.columns) + .flatMap(([columnId, column]) => { + // If we're transitioning to another operation, check for "new" incompleteColumns rather + // than "old" saved operation on the layer + const columnFinalRef = + layer.incompleteColumns?.[columnId]?.operationType || column.operationType; + const def = operationDefinitionMap[columnFinalRef]; + + if (def.getErrorMessage) { + return def.getErrorMessage(layer, columnId, indexPattern); + } + }) + // remove the undefined values + .filter((v: string | undefined): v is string => v != null); return errors.length ? errors : undefined; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts index 57cc4abeb723a..b5a4905a29738 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts @@ -7,9 +7,10 @@ import { DataType } from '../types'; import { IndexPattern, IndexPatternLayer } from './types'; import { DraggedField } from './indexpattern'; -import { +import type { BaseIndexPatternColumn, FieldBasedIndexPatternColumn, + ReferenceBasedIndexPatternColumn, } from './operations/definitions/column_types'; import { operationDefinitionMap, IndexPatternColumn } from './operations'; @@ -53,12 +54,29 @@ export function isColumnInvalid( if (!column) return; const operationDefinition = column.operationType && operationDefinitionMap[column.operationType]; - return !!( - operationDefinition.getErrorMessage && - operationDefinition.getErrorMessage(layer, columnId, indexPattern) + // check also references for errors + const referencesHaveErrors = + true && + 'references' in column && + Boolean(getReferencesErrors(layer, column, indexPattern).filter(Boolean).length); + + return ( + !!operationDefinition.getErrorMessage?.(layer, columnId, indexPattern) || referencesHaveErrors ); } +function getReferencesErrors( + layer: IndexPatternLayer, + column: ReferenceBasedIndexPatternColumn, + indexPattern: IndexPattern +) { + return column.references?.map((referenceId: string) => { + const referencedOperation = layer.columns[referenceId]?.operationType; + const referencedDefinition = operationDefinitionMap[referencedOperation]; + return referencedDefinition?.getErrorMessage?.(layer, referenceId, indexPattern); + }); +} + export function fieldIsInvalid(column: IndexPatternColumn | undefined, indexPattern: IndexPattern) { if (!column || !hasField(column)) { return false; From 6425c5d6ae343551e953c31b4d9e4ed0b231353a Mon Sep 17 00:00:00 2001 From: Thom Heymann <190132+thomheymann@users.noreply.github.com> Date: Wed, 27 Jan 2021 14:07:20 +0000 Subject: [PATCH 83/90] Fixed regex bug in Safari (#89399) * Fixed regex bug in Safari * Added extra unit test --- x-pack/plugins/security/common/constants.ts | 2 +- .../management/users/edit_user/create_user_page.test.tsx | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index c235c296bcbae..372f539d812fd 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -29,7 +29,7 @@ export const NEXT_URL_QUERY_STRING_PARAMETER = 'next'; * - Must contain only letters, numbers, spaces, punctuation and printable symbols. * - Must not contain leading or trailing spaces. */ -export const NAME_REGEX = /^(?! )[a-zA-Z0-9 !"#$%&'()*+,\-./\\:;<=>?@\[\]^_`{|}~]+(??@\[\]^_`{|}~]*[a-zA-Z0-9!"#$%&'()*+,\-./\\:;<=>?@\[\]^_`{|}~]$/; /** * Maximum length of usernames and role names. diff --git a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx index e7e3e1164ae14..99c67201b2b56 100644 --- a/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx +++ b/x-pack/plugins/security/public/management/users/edit_user/create_user_page.test.tsx @@ -88,6 +88,12 @@ describe('CreateUserPage', () => { await findAllByText(/Username must not contain leading or trailing spaces/i); + fireEvent.change(await findByLabelText('Username'), { + target: { value: 'username_with_trailing_space ' }, + }); + + await findAllByText(/Username must not contain leading or trailing spaces/i); + fireEvent.change(await findByLabelText('Username'), { target: { value: '€' }, }); From cadcbf88452165a8edf7f8c5328dc74cd200d287 Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Wed, 27 Jan 2021 08:52:44 -0600 Subject: [PATCH 84/90] [Enterprise Search] Add links to doc links service (#89260) * [Enterprise Search] Add links to doc links service * Remove extra slash * Add types * Update API docs and API review --- .../kibana-plugin-core-public.doclinksstart.links.md | 5 +++++ .../public/kibana-plugin-core-public.doclinksstart.md | 2 +- src/core/public/doc_links/doc_links_service.ts | 10 ++++++++++ src/core/public/public.api.md | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md index ff2a8a2b5f75f..79c603165cae4 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.links.md @@ -30,6 +30,11 @@ readonly links: { readonly metricbeat: { readonly base: string; }; + readonly enterpriseSearch: { + readonly base: string; + readonly appSearchBase: string; + readonly workplaceSearchBase: string; + }; readonly heartbeat: { readonly base: string; }; diff --git a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md index 8404326f773e6..f4bce8b51ebb1 100644 --- a/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.doclinksstart.md @@ -17,5 +17,5 @@ export interface DocLinksStart | --- | --- | --- | | [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | string | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | string | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: string;
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
} | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;
readonly urlDrilldownVariables: string;
};
readonly discover: Record<string, string>;
readonly filebeat: {
readonly base: string;
readonly installation: string;
readonly configuration: string;
readonly elasticsearchOutput: string;
readonly startup: string;
readonly exportedFields: string;
};
readonly auditbeat: {
readonly base: string;
};
readonly metricbeat: {
readonly base: string;
};
readonly enterpriseSearch: {
readonly base: string;
readonly appSearchBase: string;
readonly workplaceSearchBase: string;
};
readonly heartbeat: {
readonly base: string;
};
readonly logstash: {
readonly base: string;
};
readonly functionbeat: {
readonly base: string;
};
readonly winlogbeat: {
readonly base: string;
};
readonly aggs: {
readonly date_histogram: string;
readonly date_range: string;
readonly date_format_pattern: string;
readonly filter: string;
readonly filters: string;
readonly geohash_grid: string;
readonly histogram: string;
readonly ip_range: string;
readonly range: string;
readonly significant_terms: string;
readonly terms: string;
readonly avg: string;
readonly avg_bucket: string;
readonly max_bucket: string;
readonly min_bucket: string;
readonly sum_bucket: string;
readonly cardinality: string;
readonly count: string;
readonly cumulative_sum: string;
readonly derivative: string;
readonly geo_bounds: string;
readonly geo_centroid: string;
readonly max: string;
readonly median: string;
readonly min: string;
readonly moving_avg: string;
readonly percentile_ranks: string;
readonly serial_diff: string;
readonly std_dev: string;
readonly sum: string;
readonly top_hits: string;
};
readonly runtimeFields: string;
readonly scriptedFields: {
readonly scriptFields: string;
readonly scriptAggs: string;
readonly painless: string;
readonly painlessApi: string;
readonly painlessSyntax: string;
readonly luceneExpressions: string;
};
readonly indexPatterns: {
readonly loadingData: string;
readonly introduction: string;
};
readonly addData: string;
readonly kibana: string;
readonly elasticsearch: Record<string, string>;
readonly siem: {
readonly guide: string;
readonly gettingStarted: string;
};
readonly query: {
readonly eql: string;
readonly luceneQuerySyntax: string;
readonly queryDsl: string;
readonly kueryQuerySyntax: string;
};
readonly date: {
readonly dateMath: string;
};
readonly management: Record<string, string>;
readonly ml: Record<string, string>;
readonly transforms: Record<string, string>;
readonly visualize: Record<string, string>;
readonly apis: Readonly<{
createIndex: string;
createSnapshotLifecyclePolicy: string;
createRoleMapping: string;
createRoleMappingTemplates: string;
createApiKey: string;
createPipeline: string;
createTransformRequest: string;
executeWatchActionModes: string;
openIndex: string;
putComponentTemplate: string;
painlessExecute: string;
putComponentTemplateMetadata: string;
putWatch: string;
updateTransform: string;
}>;
readonly observability: Record<string, string>;
readonly alerting: Record<string, string>;
readonly maps: Record<string, string>;
readonly monitoring: Record<string, string>;
readonly security: Readonly<{
apiKeyServiceSettings: string;
clusterPrivileges: string;
elasticsearchSettings: string;
elasticsearchEnableSecurity: string;
indicesPrivileges: string;
kibanaTLS: string;
kibanaPrivileges: string;
mappingRoles: string;
mappingRolesFieldRules: string;
runAsPrivilege: string;
}>;
readonly watcher: Record<string, string>;
readonly ccs: Record<string, string>;
} | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index b82254e5a1416..c732fc7823b62 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -46,6 +46,11 @@ export class DocLinksService { auditbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/auditbeat/${DOC_LINK_VERSION}`, }, + enterpriseSearch: { + base: `${ELASTIC_WEBSITE_URL}guide/en/enterprise-search/${DOC_LINK_VERSION}`, + appSearchBase: `${ELASTIC_WEBSITE_URL}guide/en/app-search/${DOC_LINK_VERSION}`, + workplaceSearchBase: `${ELASTIC_WEBSITE_URL}guide/en/workplace-search/${DOC_LINK_VERSION}`, + }, metricbeat: { base: `${ELASTIC_WEBSITE_URL}guide/en/beats/metricbeat/${DOC_LINK_VERSION}`, }, @@ -260,6 +265,11 @@ export interface DocLinksStart { readonly metricbeat: { readonly base: string; }; + readonly enterpriseSearch: { + readonly base: string; + readonly appSearchBase: string; + readonly workplaceSearchBase: string; + }; readonly heartbeat: { readonly base: string; }; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 52fc8fbf33910..d72b2aa5afd1e 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -483,6 +483,11 @@ export interface DocLinksStart { readonly metricbeat: { readonly base: string; }; + readonly enterpriseSearch: { + readonly base: string; + readonly appSearchBase: string; + readonly workplaceSearchBase: string; + }; readonly heartbeat: { readonly base: string; }; From d1b88afd3b0c15a65158f39992d2ff7d8e129e75 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Jan 2021 17:01:02 +0200 Subject: [PATCH 85/90] Increase the time needed to locate the save viz toast (#89301) --- test/functional/page_objects/common_page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 1149f1b100788..ed817b8b55e80 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -376,7 +376,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo } async closeToast() { - const toast = await find.byCssSelector('.euiToast', 2 * defaultFindTimeout); + const toast = await find.byCssSelector('.euiToast', 6 * defaultFindTimeout); await toast.moveMouseTo(); const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText(); From 0abf45fcf1673a714c2b4fa894cf58fb9b24e265 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Jan 2021 17:03:44 +0200 Subject: [PATCH 86/90] [Vega Docs] Add experimental flag on the vega maps title (#89402) * [Vega Docs] Add experimental flag on the vega maps title * Add the experimental warning on the initial paragraph of vega maps to be more visible --- docs/user/dashboard/vega-reference.asciidoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index 99c5e62697cfd..4a0598cc569cd 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -237,7 +237,7 @@ format: {property: "features"} [[vega-with-a-map]] ==== Vega with a Map -To enable *Maps*, the graph must specify `type=map` in the host configuration: +experimental[] To enable *Maps*, the graph must specify `type=map` in the host configuration: [source,yaml] ---- @@ -335,7 +335,7 @@ Use the contextual *Inspect* tool to gain insights into different elements. [float] [[inspect-elasticsearch-requests]] -======= Inspect {es} requests +====== Inspect {es} requests *Vega* uses the {ref}/search-search.html[{es} search API] to get documents and aggregation results from {es}. To troubleshoot these requests, click *Inspect*, which shows the most recent requests. From da8d6b939a9a4780292cdc6cd243752e597b0846 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Wed, 27 Jan 2021 08:18:54 -0700 Subject: [PATCH 87/90] Migrate maps_legacy, maps_oss, region_map, and tile_map plugions to TS projects (#89351) --- src/plugins/maps_legacy/public/index.ts | 3 --- .../maps_legacy/public/map/color_util.d.ts | 11 ++++++++ .../public/map/kibana_map_layer.d.ts | 25 +++++++++++++++++++ .../maps_legacy/public/tooltip_provider.d.ts | 9 +++++++ src/plugins/maps_legacy/tsconfig.json | 14 +++++++++++ src/plugins/maps_oss/tsconfig.json | 14 +++++++++++ src/plugins/region_map/tsconfig.json | 15 +++++++++++ src/plugins/tile_map/tsconfig.json | 15 +++++++++++ tsconfig.json | 8 ++++++ tsconfig.refs.json | 4 +++ 10 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/plugins/maps_legacy/public/map/color_util.d.ts create mode 100644 src/plugins/maps_legacy/public/map/kibana_map_layer.d.ts create mode 100644 src/plugins/maps_legacy/public/tooltip_provider.d.ts create mode 100644 src/plugins/maps_legacy/tsconfig.json create mode 100644 src/plugins/maps_oss/tsconfig.json create mode 100644 src/plugins/region_map/tsconfig.json create mode 100644 src/plugins/tile_map/tsconfig.json diff --git a/src/plugins/maps_legacy/public/index.ts b/src/plugins/maps_legacy/public/index.ts index 95550fab1ba17..9268f14995f44 100644 --- a/src/plugins/maps_legacy/public/index.ts +++ b/src/plugins/maps_legacy/public/index.ts @@ -8,9 +8,7 @@ import { PluginInitializerContext } from 'kibana/public'; import { MapsLegacyPlugin } from './plugin'; -// @ts-ignore import * as colorUtil from './map/color_util'; -// @ts-ignore import { KibanaMapLayer } from './map/kibana_map_layer'; import { VectorLayer, @@ -19,7 +17,6 @@ import { TmsLayer, IServiceSettings, } from './map/service_settings_types'; -// @ts-ignore import { mapTooltipProvider } from './tooltip_provider'; import './map/index.scss'; diff --git a/src/plugins/maps_legacy/public/map/color_util.d.ts b/src/plugins/maps_legacy/public/map/color_util.d.ts new file mode 100644 index 0000000000000..9ec6b3c1fb007 --- /dev/null +++ b/src/plugins/maps_legacy/public/map/color_util.d.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export function getLegendColors(colorRamp: unknown, numLegendColors?: number): string[]; + +export function getColor(colorRamp: unknown, i: number): string; diff --git a/src/plugins/maps_legacy/public/map/kibana_map_layer.d.ts b/src/plugins/maps_legacy/public/map/kibana_map_layer.d.ts new file mode 100644 index 0000000000000..222cb6b215f9a --- /dev/null +++ b/src/plugins/maps_legacy/public/map/kibana_map_layer.d.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export class KibanaMapLayer { + constructor(); + + getBounds(): Promise; + + addToLeafletMap(leafletMap: unknown): void; + + removeFromLeafletMap(leafletMap: unknown): void; + + appendLegendContents(): void; + + updateExtent(): void; + + movePointer(): void; + + getAttributions(): unknown; +} diff --git a/src/plugins/maps_legacy/public/tooltip_provider.d.ts b/src/plugins/maps_legacy/public/tooltip_provider.d.ts new file mode 100644 index 0000000000000..4082a6ef83c4d --- /dev/null +++ b/src/plugins/maps_legacy/public/tooltip_provider.d.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export function mapTooltipProvider(element: unknown, formatter: unknown): () => unknown; diff --git a/src/plugins/maps_legacy/tsconfig.json b/src/plugins/maps_legacy/tsconfig.json new file mode 100644 index 0000000000000..e7ea06706b64f --- /dev/null +++ b/src/plugins/maps_legacy/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["common/**/*", "public/**/*", "server/**/*", "config.ts"], + "references": [ + { "path": "../vis_default_editor/tsconfig.json" }, + ] +} diff --git a/src/plugins/maps_oss/tsconfig.json b/src/plugins/maps_oss/tsconfig.json new file mode 100644 index 0000000000000..03c30c3c49fd3 --- /dev/null +++ b/src/plugins/maps_oss/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["common/**/*", "public/**/*", "server/**/*", "config.ts"], + "references": [ + { "path": "../visualizations/tsconfig.json" }, + ] +} diff --git a/src/plugins/region_map/tsconfig.json b/src/plugins/region_map/tsconfig.json new file mode 100644 index 0000000000000..40f76ece2a6ff --- /dev/null +++ b/src/plugins/region_map/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["public/**/*", "server/**/*"], + "references": [ + { "path": "../maps_legacy/tsconfig.json" }, + { "path": "../vis_default_editor/tsconfig.json" }, + ] +} diff --git a/src/plugins/tile_map/tsconfig.json b/src/plugins/tile_map/tsconfig.json new file mode 100644 index 0000000000000..40f76ece2a6ff --- /dev/null +++ b/src/plugins/tile_map/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true + }, + "include": ["public/**/*", "server/**/*"], + "references": [ + { "path": "../maps_legacy/tsconfig.json" }, + { "path": "../vis_default_editor/tsconfig.json" }, + ] +} diff --git a/tsconfig.json b/tsconfig.json index e7856aa0c8747..334a3febfddda 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,8 +27,11 @@ "src/plugins/kibana_usage_collection/**/*", "src/plugins/kibana_utils/**/*", "src/plugins/management/**/*", + "src/plugins/maps_legacy/**/*", + "src/plugins/maps_oss/**/*", "src/plugins/navigation/**/*", "src/plugins/newsfeed/**/*", + "src/plugins/region_map/**/*", "src/plugins/saved_objects/**/*", "src/plugins/saved_objects_management/**/*", "src/plugins/saved_objects_tagging_oss/**/*", @@ -37,6 +40,7 @@ "src/plugins/spaces_oss/**/*", "src/plugins/telemetry/**/*", "src/plugins/telemetry_collection_manager/**/*", + "src/plugins/tile_map/**/*", "src/plugins/timelion/**/*", "src/plugins/ui_actions/**/*", "src/plugins/url_forwarding/**/*", @@ -81,8 +85,11 @@ { "path": "./src/plugins/kibana_usage_collection/tsconfig.json" }, { "path": "./src/plugins/kibana_utils/tsconfig.json" }, { "path": "./src/plugins/management/tsconfig.json" }, + { "path": "./src/plugins/maps_legacy/tsconfig.json" }, + { "path": "./src/plugins/maps_oss/tsconfig.json" }, { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, + { "path": "./src/plugins/region_map/tsconfig.json" }, { "path": "./src/plugins/saved_objects/tsconfig.json" }, { "path": "./src/plugins/saved_objects_management/tsconfig.json" }, { "path": "./src/plugins/saved_objects_tagging_oss/tsconfig.json" }, @@ -91,6 +98,7 @@ { "path": "./src/plugins/spaces_oss/tsconfig.json" }, { "path": "./src/plugins/telemetry/tsconfig.json" }, { "path": "./src/plugins/telemetry_collection_manager/tsconfig.json" }, + { "path": "./src/plugins/tile_map/tsconfig.json" }, { "path": "./src/plugins/timelion/tsconfig.json" }, { "path": "./src/plugins/ui_actions/tsconfig.json" }, { "path": "./src/plugins/url_forwarding/tsconfig.json" }, diff --git a/tsconfig.refs.json b/tsconfig.refs.json index 5edfd4231a6d5..a8eecd278160c 100644 --- a/tsconfig.refs.json +++ b/tsconfig.refs.json @@ -23,8 +23,11 @@ { "path": "./src/plugins/kibana_usage_collection/tsconfig.json" }, { "path": "./src/plugins/kibana_utils/tsconfig.json" }, { "path": "./src/plugins/management/tsconfig.json" }, + { "path": "./src/plugins/maps_legacy/tsconfig.json" }, + { "path": "./src/plugins/maps_oss/tsconfig.json" }, { "path": "./src/plugins/navigation/tsconfig.json" }, { "path": "./src/plugins/newsfeed/tsconfig.json" }, + { "path": "./src/plugins/region_map/tsconfig.json" }, { "path": "./src/plugins/saved_objects/tsconfig.json" }, { "path": "./src/plugins/saved_objects_management/tsconfig.json" }, { "path": "./src/plugins/saved_objects_tagging_oss/tsconfig.json" }, @@ -34,6 +37,7 @@ { "path": "./src/plugins/spaces_oss/tsconfig.json" }, { "path": "./src/plugins/telemetry/tsconfig.json" }, { "path": "./src/plugins/telemetry_collection_manager/tsconfig.json" }, + { "path": "./src/plugins/tile_map/tsconfig.json" }, { "path": "./src/plugins/timelion/tsconfig.json" }, { "path": "./src/plugins/ui_actions/tsconfig.json" }, { "path": "./src/plugins/url_forwarding/tsconfig.json" }, From 007e7e4506a9256c62446a8035ae22160e69f203 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Wed, 27 Jan 2021 10:22:36 -0500 Subject: [PATCH 88/90] [Upgrade Assistant] Migrate server to new es-js client (#89207) --- .../plugins/upgrade_assistant/common/types.ts | 19 +- .../components/tabs/checkup/constants.tsx | 3 +- .../components/tabs/checkup/controls.tsx | 3 +- .../checkup/deprecations/grouped.test.tsx | 4 +- .../tabs/checkup/deprecations/grouped.tsx | 4 +- .../tabs/checkup/deprecations/health.tsx | 3 +- .../tabs/checkup/deprecations/list.tsx | 4 +- .../tabs/checkup/filter_bar.test.tsx | 3 +- .../components/tabs/checkup/filter_bar.tsx | 3 +- .../lib/es_deprecation_logging_apis.test.ts | 14 +- .../server/lib/es_deprecation_logging_apis.ts | 12 +- .../server/lib/es_indices_state_check.ts | 13 +- .../server/lib/es_migration_apis.test.ts | 111 +++-- .../server/lib/es_migration_apis.ts | 24 +- .../server/lib/es_version_precheck.test.ts | 94 ++-- .../server/lib/es_version_precheck.ts | 22 +- .../lib/reindexing/reindex_actions.test.ts | 33 +- .../server/lib/reindexing/reindex_actions.ts | 13 +- .../lib/reindexing/reindex_service.test.ts | 418 ++++++++++-------- .../server/lib/reindexing/reindex_service.ts | 107 ++--- .../server/lib/reindexing/worker.ts | 13 +- .../lib/telemetry/usage_collector.test.ts | 20 +- .../server/lib/telemetry/usage_collector.ts | 17 +- .../server/routes/__mocks__/routes.mock.ts | 4 +- .../server/routes/cluster_checkup.test.ts | 2 +- .../server/routes/cluster_checkup.ts | 12 +- .../server/routes/deprecation_logging.test.ts | 24 +- .../server/routes/deprecation_logging.ts | 8 +- .../routes/reindex_indices/reindex_handler.ts | 6 +- .../routes/reindex_indices/reindex_indices.ts | 37 +- .../upgrade_assistant/upgrade_assistant.ts | 2 +- .../upgrade_assistant_integration/config.js | 2 - .../services/index.js | 7 - .../services/legacy_es.js | 18 - .../upgrade_assistant/reindexing.js | 22 +- 35 files changed, 570 insertions(+), 531 deletions(-) delete mode 100644 x-pack/test/upgrade_assistant_integration/services/index.js delete mode 100644 x-pack/test/upgrade_assistant_integration/services/legacy_es.js diff --git a/x-pack/plugins/upgrade_assistant/common/types.ts b/x-pack/plugins/upgrade_assistant/common/types.ts index 2a94b39ca6c66..9625ca89b11d0 100644 --- a/x-pack/plugins/upgrade_assistant/common/types.ts +++ b/x-pack/plugins/upgrade_assistant/common/types.ts @@ -5,8 +5,6 @@ */ import { SavedObject, SavedObjectAttributes } from 'src/core/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../src/core/server/elasticsearch/legacy/api_types'; export enum ReindexStep { // Enum values are spaced out by 10 to give us room to insert steps in between. @@ -165,6 +163,23 @@ export interface UpgradeAssistantTelemetrySavedObjectAttributes { [key: string]: any; } +export type MIGRATION_DEPRECATION_LEVEL = 'none' | 'info' | 'warning' | 'critical'; +export interface DeprecationInfo { + level: MIGRATION_DEPRECATION_LEVEL; + message: string; + url: string; + details?: string; +} + +export interface IndexSettingsDeprecationInfo { + [indexName: string]: DeprecationInfo[]; +} +export interface DeprecationAPIResponse { + cluster_settings: DeprecationInfo[]; + ml_settings: DeprecationInfo[]; + node_settings: DeprecationInfo[]; + index_settings: IndexSettingsDeprecationInfo; +} export interface EnrichedDeprecationInfo extends DeprecationInfo { index?: string; node?: string; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/constants.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/constants.tsx index 66c802097055b..8637099b77c9b 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/constants.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/constants.tsx @@ -6,8 +6,7 @@ import { IconColor } from '@elastic/eui'; import { invert } from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; +import { DeprecationInfo } from '../../../../../common/types'; export const LEVEL_MAP: { [level: string]: number } = { warning: 0, diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/controls.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/controls.tsx index d75a25a95d67f..c75db7e2f96e1 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/controls.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/controls.tsx @@ -8,8 +8,7 @@ import React, { FunctionComponent, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; +import { DeprecationInfo } from '../../../../../common/types'; import { GroupByOption, LevelFilterOption, LoadingState } from '../../types'; import { FilterBar } from './filter_bar'; import { GroupByBar } from './group_by_bar'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx index 6bdb5df036224..727d959f49a71 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.test.tsx @@ -9,9 +9,7 @@ import React from 'react'; import { mountWithIntl, shallowWithIntl } from '@kbn/test/jest'; import { EuiBadge, EuiPagination } from '@elastic/eui'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; -import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { DeprecationInfo, EnrichedDeprecationInfo } from '../../../../../../common/types'; import { GroupByOption, LevelFilterOption } from '../../../types'; import { DeprecationAccordion, filterDeps, GroupedDeprecations } from './grouped'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx index de1a5a996d75f..20ffbae143672 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/grouped.tsx @@ -18,9 +18,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; -import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { DeprecationInfo, EnrichedDeprecationInfo } from '../../../../../../common/types'; import { GroupByOption, LevelFilterOption } from '../../../types'; import { DeprecationCountSummary } from './count_summary'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/health.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/health.tsx index 3ce40d0c4fdf0..c866c1e1f6847 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/health.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/health.tsx @@ -10,8 +10,7 @@ import React, { FunctionComponent } from 'react'; import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; +import { DeprecationInfo } from '../../../../../../common/types'; import { COLOR_MAP, LEVEL_MAP, REVERSE_LEVEL_MAP } from '../constants'; const LocalizedLevels: { [level: string]: string } = { diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx index 038f05aace4c3..afc443b8c3b7d 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/deprecations/list.tsx @@ -6,9 +6,7 @@ import React, { FunctionComponent } from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; -import { EnrichedDeprecationInfo } from '../../../../../../common/types'; +import { DeprecationInfo, EnrichedDeprecationInfo } from '../../../../../../common/types'; import { GroupByOption } from '../../../types'; import { COLOR_MAP, LEVEL_MAP } from '../constants'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.test.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.test.tsx index 053ef21d6b309..231b15fc52d72 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.test.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.test.tsx @@ -6,9 +6,8 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; +import { DeprecationInfo } from '../../../../../common/types'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; import { LevelFilterOption } from '../../types'; import { FilterBar } from './filter_bar'; diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.tsx index 6939c547fee57..abcd02d5a5ce4 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/tabs/checkup/filter_bar.tsx @@ -10,8 +10,7 @@ import React from 'react'; import { EuiFilterButton, EuiFilterGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationInfo } from '../../../../../../../../src/core/server/elasticsearch/legacy/api_types'; +import { DeprecationInfo } from '../../../../../common/types'; import { LevelFilterOption } from '../../types'; const LocalizedOptions: { [option: string]: string } = { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts index b0dec299b2b12..dee05c97f11af 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.test.ts @@ -12,10 +12,10 @@ import { describe('getDeprecationLoggingStatus', () => { it('calls cluster.getSettings', async () => { - const dataClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + const dataClient = elasticsearchServiceMock.createScopedClusterClient(); await getDeprecationLoggingStatus(dataClient); - expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.getSettings', { - includeDefaults: true, + expect(dataClient.asCurrentUser.cluster.getSettings).toHaveBeenCalledWith({ + include_defaults: true, }); }); }); @@ -23,9 +23,9 @@ describe('getDeprecationLoggingStatus', () => { describe('setDeprecationLogging', () => { describe('isEnabled = true', () => { it('calls cluster.putSettings with logger.deprecation = WARN', async () => { - const dataClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + const dataClient = elasticsearchServiceMock.createScopedClusterClient(); await setDeprecationLogging(dataClient, true); - expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.putSettings', { + expect(dataClient.asCurrentUser.cluster.putSettings).toHaveBeenCalledWith({ body: { transient: { 'logger.deprecation': 'WARN' } }, }); }); @@ -33,9 +33,9 @@ describe('setDeprecationLogging', () => { describe('isEnabled = false', () => { it('calls cluster.putSettings with logger.deprecation = ERROR', async () => { - const dataClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + const dataClient = elasticsearchServiceMock.createScopedClusterClient(); await setDeprecationLogging(dataClient, false); - expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('cluster.putSettings', { + expect(dataClient.asCurrentUser.cluster.putSettings).toHaveBeenCalledWith({ body: { transient: { 'logger.deprecation': 'ERROR' } }, }); }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts index 348eebb97e384..c545d12ac1b82 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_deprecation_logging_apis.ts @@ -4,17 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { ILegacyScopedClusterClient } from 'src/core/server'; +import { IScopedClusterClient } from 'src/core/server'; interface DeprecationLoggingStatus { isEnabled: boolean; } export async function getDeprecationLoggingStatus( - dataClient: ILegacyScopedClusterClient + dataClient: IScopedClusterClient ): Promise { - const response = await dataClient.callAsCurrentUser('cluster.getSettings', { - includeDefaults: true, + const { body: response } = await dataClient.asCurrentUser.cluster.getSettings({ + include_defaults: true, }); return { @@ -23,10 +23,10 @@ export async function getDeprecationLoggingStatus( } export async function setDeprecationLogging( - dataClient: ILegacyScopedClusterClient, + dataClient: IScopedClusterClient, isEnabled: boolean ): Promise { - const response = await dataClient.callAsCurrentUser('cluster.putSettings', { + const { body: response } = await dataClient.asCurrentUser.cluster.putSettings({ body: { transient: { 'logger.deprecation': isEnabled ? 'WARN' : 'ERROR', diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_indices_state_check.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_indices_state_check.ts index bce48b152700f..739499e2235b5 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_indices_state_check.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_indices_state_check.ts @@ -4,22 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller } from 'kibana/server'; +import { ElasticsearchClient } from 'kibana/server'; import { getIndexState } from '../../common/get_index_state'; import { ResolveIndexResponseFromES } from '../../common/types'; type StatusCheckResult = Record; export const esIndicesStateCheck = async ( - callAsUser: LegacyAPICaller, + asCurrentUser: ElasticsearchClient, indices: string[] ): Promise => { - const response: ResolveIndexResponseFromES = await callAsUser('transport.request', { - method: 'GET', - path: `/_resolve/index/*`, - query: { - expand_wildcards: 'all', - }, + const { body: response } = await asCurrentUser.indices.resolveIndex({ + name: '*', + expand_wildcards: 'all', }); const result: StatusCheckResult = {}; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts index 312a7275382b8..6e8a729b4d3bb 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.test.ts @@ -5,95 +5,94 @@ */ import _ from 'lodash'; +import { RequestEvent } from '@elastic/elasticsearch/lib/Transport'; import { elasticsearchServiceMock } from 'src/core/server/mocks'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationAPIResponse } from '../../../../../src/core/server/elasticsearch/legacy/api_types'; +import { DeprecationAPIResponse } from '../../common/types'; import { getUpgradeAssistantStatus } from './es_migration_apis'; import fakeDeprecations from './__fixtures__/fake_deprecations.json'; const fakeIndexNames = Object.keys(fakeDeprecations.index_settings); +const asApiResponse = (body: T): RequestEvent => + ({ + body, + } as RequestEvent); + describe('getUpgradeAssistantStatus', () => { const resolvedIndices = { indices: fakeIndexNames.map((f) => ({ name: f, attributes: ['open'] })), }; - let deprecationsResponse: DeprecationAPIResponse; - - const dataClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); - (dataClient.callAsCurrentUser as jest.Mock).mockImplementation(async (api, { path }) => { - if (path === '/_migration/deprecations') { - return deprecationsResponse; - } else if (path === '/_resolve/index/*') { - return resolvedIndices; - } else if (api === 'indices.getMapping') { - return {}; - } else { - throw new Error(`Unexpected API call: ${path}`); - } - }); + // @ts-expect-error mock data is too loosely typed + const deprecationsResponse: DeprecationAPIResponse = _.cloneDeep(fakeDeprecations); - beforeEach(() => { - // @ts-expect-error mock data is too loosely typed - deprecationsResponse = _.cloneDeep(fakeDeprecations); - }); + const esClient = elasticsearchServiceMock.createScopedClusterClient(); + + esClient.asCurrentUser.migration.deprecations.mockResolvedValue( + asApiResponse(deprecationsResponse) + ); + + esClient.asCurrentUser.indices.resolveIndex.mockResolvedValue(asApiResponse(resolvedIndices)); it('calls /_migration/deprecations', async () => { - await getUpgradeAssistantStatus(dataClient, false); - expect(dataClient.callAsCurrentUser).toHaveBeenCalledWith('transport.request', { - path: '/_migration/deprecations', - method: 'GET', - }); + await getUpgradeAssistantStatus(esClient, false); + expect(esClient.asCurrentUser.migration.deprecations).toHaveBeenCalled(); }); it('returns the correct shape of data', async () => { - const resp = await getUpgradeAssistantStatus(dataClient, false); + const resp = await getUpgradeAssistantStatus(esClient, false); expect(resp).toMatchSnapshot(); }); it('returns readyForUpgrade === false when critical issues found', async () => { - deprecationsResponse = { - cluster_settings: [{ level: 'critical', message: 'Do count me', url: 'https://...' }], - node_settings: [], - ml_settings: [], - index_settings: {}, - }; - - await expect(getUpgradeAssistantStatus(dataClient, false)).resolves.toHaveProperty( + esClient.asCurrentUser.migration.deprecations.mockResolvedValue( + asApiResponse({ + cluster_settings: [{ level: 'critical', message: 'Do count me', url: 'https://...' }], + node_settings: [], + ml_settings: [], + index_settings: {}, + }) + ); + + await expect(getUpgradeAssistantStatus(esClient, false)).resolves.toHaveProperty( 'readyForUpgrade', false ); }); it('returns readyForUpgrade === true when no critical issues found', async () => { - deprecationsResponse = { - cluster_settings: [{ level: 'warning', message: 'Do not count me', url: 'https://...' }], - node_settings: [], - ml_settings: [], - index_settings: {}, - }; - - await expect(getUpgradeAssistantStatus(dataClient, false)).resolves.toHaveProperty( + esClient.asCurrentUser.migration.deprecations.mockResolvedValue( + asApiResponse({ + cluster_settings: [{ level: 'warning', message: 'Do not count me', url: 'https://...' }], + node_settings: [], + ml_settings: [], + index_settings: {}, + }) + ); + + await expect(getUpgradeAssistantStatus(esClient, false)).resolves.toHaveProperty( 'readyForUpgrade', true ); }); it('filters out security realm deprecation on Cloud', async () => { - deprecationsResponse = { - cluster_settings: [ - { - level: 'critical', - message: 'Security realm settings structure changed', - url: 'https://...', - }, - ], - node_settings: [], - ml_settings: [], - index_settings: {}, - }; - - const result = await getUpgradeAssistantStatus(dataClient, true); + esClient.asCurrentUser.migration.deprecations.mockResolvedValue( + asApiResponse({ + cluster_settings: [ + { + level: 'critical', + message: 'Security realm settings structure changed', + url: 'https://...', + }, + ], + node_settings: [], + ml_settings: [], + index_settings: {}, + }) + ); + + const result = await getUpgradeAssistantStatus(esClient, true); expect(result).toHaveProperty('readyForUpgrade', true); expect(result).toHaveProperty('cluster', []); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts index 9f55b9d049735..22b9956fc957a 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_migration_apis.ts @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ILegacyScopedClusterClient } from 'src/core/server'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import type { DeprecationAPIResponse } from '../../../../../src/core/server/elasticsearch/legacy/api_types'; -import { EnrichedDeprecationInfo, UpgradeAssistantStatus } from '../../common/types'; +import { IScopedClusterClient } from 'src/core/server'; +import { + DeprecationAPIResponse, + EnrichedDeprecationInfo, + UpgradeAssistantStatus, +} from '../../common/types'; import { esIndicesStateCheck } from './es_indices_state_check'; export async function getUpgradeAssistantStatus( - dataClient: ILegacyScopedClusterClient, + dataClient: IScopedClusterClient, isCloudEnabled: boolean ): Promise { - const deprecations = await dataClient.callAsCurrentUser('transport.request', { - path: '/_migration/deprecations', - method: 'GET', - }); + const { + body: deprecations, + } = await dataClient.asCurrentUser.migration.deprecations(); const cluster = getClusterDeprecations(deprecations, isCloudEnabled); const indices = getCombinedIndexInfos(deprecations); @@ -28,10 +29,7 @@ export async function getUpgradeAssistantStatus( // If we have found deprecation information for index/indices check whether the index is // open or closed. if (indexNames.length) { - const indexStates = await esIndicesStateCheck( - dataClient.callAsCurrentUser.bind(dataClient), - indexNames - ); + const indexStates = await esIndicesStateCheck(dataClient.asCurrentUser, indexNames); indices.forEach((indexData) => { indexData.blockerForReindexing = diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts index 78f159ed98867..2310f993ce27d 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.test.ts @@ -5,7 +5,7 @@ */ import { SemVer } from 'semver'; -import { ILegacyScopedClusterClient, kibanaResponseFactory } from 'src/core/server'; +import { IScopedClusterClient, kibanaResponseFactory } from 'src/core/server'; import { xpackMocks } from '../../../../mocks'; import { CURRENT_VERSION } from '../../common/version'; import { @@ -17,14 +17,20 @@ import { describe('getAllNodeVersions', () => { it('returns a list of unique node versions', async () => { const adminClient = ({ - callAsInternalUser: jest.fn().mockResolvedValue({ + asInternalUser: { nodes: { - node1: { version: '7.0.0' }, - node2: { version: '7.0.0' }, - node3: { version: '6.0.0' }, + info: jest.fn().mockResolvedValue({ + body: { + nodes: { + node1: { version: '7.0.0' }, + node2: { version: '7.0.0' }, + node3: { version: '6.0.0' }, + }, + }, + }), }, - }), - } as unknown) as ILegacyScopedClusterClient; + }, + } as unknown) as IScopedClusterClient; await expect(getAllNodeVersions(adminClient)).resolves.toEqual([ new SemVer('6.0.0'), @@ -73,12 +79,18 @@ describe('verifyAllMatchKibanaVersion', () => { describe('EsVersionPrecheck', () => { it('returns a 403 when callCluster fails with a 403', async () => { - const fakeCall = jest.fn().mockRejectedValue({ status: 403 }); + const fakeCall = jest.fn().mockRejectedValue({ statusCode: 403 }); const ctx = xpackMocks.createRequestHandlerContext(); - ctx.core.elasticsearch.legacy.client = { - callAsCurrentUser: jest.fn(), - callAsInternalUser: fakeCall, + ctx.core.elasticsearch.client = { + asInternalUser: { + ...ctx.core.elasticsearch.client.asInternalUser, + nodes: { + ...ctx.core.elasticsearch.client.asInternalUser.nodes, + info: fakeCall, + }, + }, + asCurrentUser: ctx.core.elasticsearch.client.asCurrentUser, }; const result = await esVersionCheck(ctx, kibanaResponseFactory); @@ -87,14 +99,22 @@ describe('EsVersionPrecheck', () => { it('returns a 426 message w/ allNodesUpgraded = false when nodes are not on same version', async () => { const ctx = xpackMocks.createRequestHandlerContext(); - ctx.core.elasticsearch.legacy.client = { - callAsCurrentUser: jest.fn(), - callAsInternalUser: jest.fn().mockResolvedValue({ + ctx.core.elasticsearch.client = { + asInternalUser: { + ...ctx.core.elasticsearch.client.asInternalUser, nodes: { - node1: { version: CURRENT_VERSION.raw }, - node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + ...ctx.core.elasticsearch.client.asInternalUser.nodes, + info: jest.fn().mockResolvedValue({ + body: { + nodes: { + node1: { version: CURRENT_VERSION.raw }, + node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + }, + }, + }), }, - }), + }, + asCurrentUser: ctx.core.elasticsearch.client.asCurrentUser, }; const result = await esVersionCheck(ctx, kibanaResponseFactory); @@ -104,14 +124,22 @@ describe('EsVersionPrecheck', () => { it('returns a 426 message w/ allNodesUpgraded = true when nodes are on next version', async () => { const ctx = xpackMocks.createRequestHandlerContext(); - ctx.core.elasticsearch.legacy.client = { - callAsCurrentUser: jest.fn(), - callAsInternalUser: jest.fn().mockResolvedValue({ + ctx.core.elasticsearch.client = { + asInternalUser: { + ...ctx.core.elasticsearch.client.asInternalUser, nodes: { - node1: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, - node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + ...ctx.core.elasticsearch.client.asInternalUser.nodes, + info: jest.fn().mockResolvedValue({ + body: { + nodes: { + node1: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + node2: { version: new SemVer(CURRENT_VERSION.raw).inc('major').raw }, + }, + }, + }), }, - }), + }, + asCurrentUser: ctx.core.elasticsearch.client.asCurrentUser, }; const result = await esVersionCheck(ctx, kibanaResponseFactory); @@ -121,14 +149,22 @@ describe('EsVersionPrecheck', () => { it('returns undefined when nodes are on same version', async () => { const ctx = xpackMocks.createRequestHandlerContext(); - ctx.core.elasticsearch.legacy.client = { - callAsCurrentUser: jest.fn(), - callAsInternalUser: jest.fn().mockResolvedValue({ + ctx.core.elasticsearch.client = { + asInternalUser: { + ...ctx.core.elasticsearch.client.asInternalUser, nodes: { - node1: { version: CURRENT_VERSION.raw }, - node2: { version: CURRENT_VERSION.raw }, + ...ctx.core.elasticsearch.client.asInternalUser.nodes, + info: jest.fn().mockResolvedValue({ + body: { + nodes: { + node1: { version: CURRENT_VERSION.raw }, + node2: { version: CURRENT_VERSION.raw }, + }, + }, + }), }, - }), + }, + asCurrentUser: ctx.core.elasticsearch.client.asCurrentUser, }; await expect(esVersionCheck(ctx, kibanaResponseFactory)).resolves.toBe(undefined); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.ts b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.ts index 2b49d4c286f61..be6c4f5ff0230 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/es_version_precheck.ts @@ -7,7 +7,7 @@ import { uniq } from 'lodash'; import { SemVer } from 'semver'; import { - ILegacyScopedClusterClient, + IScopedClusterClient, KibanaRequest, KibanaResponseFactory, RequestHandler, @@ -15,14 +15,22 @@ import { } from 'src/core/server'; import { CURRENT_VERSION } from '../../common/version'; +interface Nodes { + nodes: { + [nodeId: string]: { version: string }; + }; +} + /** * Returns an array of all the unique Elasticsearch Node Versions in the Elasticsearch cluster. */ -export const getAllNodeVersions = async (adminClient: ILegacyScopedClusterClient) => { +export const getAllNodeVersions = async (adminClient: IScopedClusterClient) => { // Get the version information for all nodes in the cluster. - const { nodes } = (await adminClient.callAsInternalUser('nodes.info', { - filterPath: 'nodes.*.version', - })) as { nodes: { [nodeId: string]: { version: string } } }; + const response = await adminClient.asInternalUser.nodes.info({ + filter_path: 'nodes.*.version', + }); + + const nodes = response.body.nodes; const versionStrings = Object.values(nodes).map(({ version }) => version); @@ -62,13 +70,13 @@ export const esVersionCheck = async ( ctx: RequestHandlerContext, response: KibanaResponseFactory ) => { - const { client } = ctx.core.elasticsearch.legacy; + const { client } = ctx.core.elasticsearch; let allNodeVersions: SemVer[]; try { allNodeVersions = await getAllNodeVersions(client); } catch (e) { - if (e.status === 403) { + if (e.statusCode === 403) { return response.forbidden({ body: e.message }); } diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts index 525c3781be749..d059c03bcecb1 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.test.ts @@ -3,7 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsErrorHelpers } from '../../../../../../src/core/server'; +import { RequestEvent } from '@elastic/elasticsearch/lib/Transport'; +import { SavedObjectsErrorHelpers } from 'src/core/server'; +import { elasticsearchServiceMock } from 'src/core/server/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ScopedClusterClientMock } from 'src/core/server/elasticsearch/client/mocks'; import moment from 'moment'; import { @@ -18,7 +22,7 @@ import { LOCK_WINDOW, ReindexActions, reindexActionsFactory } from './reindex_ac describe('ReindexActions', () => { let client: jest.Mocked; - let callCluster: jest.Mock; + let clusterClient: ScopedClusterClientMock; let actions: ReindexActions; const unimplemented = (name: string) => () => @@ -38,8 +42,8 @@ describe('ReindexActions', () => { Promise.resolve({ id, attributes } as ReindexSavedObject) ) as any, }; - callCluster = jest.fn(); - actions = reindexActionsFactory(client, callCluster); + clusterClient = elasticsearchServiceMock.createScopedClusterClient(); + actions = reindexActionsFactory(client, clusterClient.asCurrentUser); }); describe('createReindexOp', () => { @@ -281,13 +285,20 @@ describe('ReindexActions', () => { }); describe('getFlatSettings', () => { + const asApiResponse = (body: T): RequestEvent => + ({ + body, + } as RequestEvent); + it('returns flat settings', async () => { - callCluster.mockResolvedValueOnce({ - myIndex: { - settings: { 'index.mySetting': '1' }, - mappings: {}, - }, - }); + clusterClient.asCurrentUser.indices.getSettings.mockResolvedValueOnce( + asApiResponse({ + myIndex: { + settings: { 'index.mySetting': '1' }, + mappings: {}, + }, + }) + ); await expect(actions.getFlatSettings('myIndex')).resolves.toEqual({ settings: { 'index.mySetting': '1' }, mappings: {}, @@ -295,7 +306,7 @@ describe('ReindexActions', () => { }); it('returns null if index does not exist', async () => { - callCluster.mockResolvedValueOnce({}); + clusterClient.asCurrentUser.indices.getSettings.mockResolvedValueOnce(asApiResponse({})); await expect(actions.getFlatSettings('myIndex')).resolves.toBeNull(); }); }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts index 6d8afee1ff950..611ab3c92b72b 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_actions.ts @@ -9,7 +9,7 @@ import moment from 'moment'; import { SavedObjectsFindResponse, SavedObjectsClientContract, - LegacyAPICaller, + ElasticsearchClient, } from 'src/core/server'; import { IndexGroup, @@ -116,7 +116,7 @@ export interface ReindexActions { export const reindexActionsFactory = ( client: SavedObjectsClientContract, - callAsUser: LegacyAPICaller + esClient: ElasticsearchClient ): ReindexActions => { // ----- Internal functions const isLocked = (reindexOp: ReindexSavedObject) => { @@ -236,9 +236,12 @@ export const reindexActionsFactory = ( }, async getFlatSettings(indexName: string) { - const flatSettings = (await callAsUser('transport.request', { - path: `/${encodeURIComponent(indexName)}?flat_settings=true`, - })) as { [indexName: string]: FlatSettings }; + const { body: flatSettings } = await esClient.indices.getSettings<{ + [indexName: string]: FlatSettings; + }>({ + index: indexName, + flat_settings: true, + }); if (!flatSettings[indexName]) { return null; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts index dea9974791a88..8a7033c1594da 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.test.ts @@ -5,8 +5,11 @@ */ jest.mock('../es_indices_state_check', () => ({ esIndicesStateCheck: jest.fn() })); import { BehaviorSubject } from 'rxjs'; +import { RequestEvent } from '@elastic/elasticsearch/lib/Transport'; import { Logger } from 'src/core/server'; -import { loggingSystemMock } from 'src/core/server/mocks'; +import { elasticsearchServiceMock, loggingSystemMock } from 'src/core/server/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ScopedClusterClientMock } from 'src/core/server/elasticsearch/client/mocks'; import { IndexGroup, @@ -28,9 +31,14 @@ import { reindexServiceFactory, } from './reindex_service'; +const asApiResponse = (body: T): RequestEvent => + ({ + body, + } as RequestEvent); + describe('reindexService', () => { let actions: jest.Mocked; - let callCluster: jest.Mock; + let clusterClient: ScopedClusterClientMock; let log: Logger; let service: ReindexService; let licensingPluginSetup: LicensingPluginSetup; @@ -59,7 +67,7 @@ describe('reindexService', () => { decrementIndexGroupReindexes: jest.fn(unimplemented('decrementIndexGroupReindexes')), runWhileIndexGroupLocked: jest.fn(async (group: string, f: any) => f({ attributes: {} })), }; - callCluster = jest.fn(); + clusterClient = elasticsearchServiceMock.createScopedClusterClient(); log = loggingSystemMock.create().get(); licensingPluginSetup = licensingMock.createSetup(); licensingPluginSetup.license$ = new BehaviorSubject( @@ -68,7 +76,12 @@ describe('reindexService', () => { }) ); - service = reindexServiceFactory(callCluster as any, actions, log, licensingPluginSetup); + service = reindexServiceFactory( + clusterClient.asCurrentUser, + actions, + log, + licensingPluginSetup + ); }); describe('hasRequiredPrivileges', () => { @@ -83,13 +96,13 @@ describe('reindexService', () => { }); it('calls security API with basic requirements', async () => { - callCluster.mockResolvedValueOnce({ has_all_requested: true }); + clusterClient.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce( + asApiResponse({ has_all_requested: true }) + ); const hasRequired = await service.hasRequiredPrivileges('anIndex'); expect(hasRequired).toBe(true); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_security/user/_has_privileges', - method: 'POST', + expect(clusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ body: { cluster: ['manage'], index: [ @@ -108,12 +121,12 @@ describe('reindexService', () => { }); it('includes manage_ml for ML indices', async () => { - callCluster.mockResolvedValueOnce({ has_all_requested: true }); + clusterClient.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce( + asApiResponse({ has_all_requested: true }) + ); await service.hasRequiredPrivileges('.ml-anomalies'); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_security/user/_has_privileges', - method: 'POST', + expect(clusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ body: { cluster: ['manage', 'manage_ml'], index: [ @@ -132,15 +145,15 @@ describe('reindexService', () => { }); it('includes checking for permissions on the baseName which could be an alias', async () => { - callCluster.mockResolvedValueOnce({ has_all_requested: true }); + clusterClient.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce( + asApiResponse({ has_all_requested: true }) + ); const hasRequired = await service.hasRequiredPrivileges( `reindexed-v${PREV_MAJOR_VERSION}-anIndex` ); expect(hasRequired).toBe(true); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_security/user/_has_privileges', - method: 'POST', + expect(clusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ body: { cluster: ['manage'], index: [ @@ -163,12 +176,14 @@ describe('reindexService', () => { }); it('includes manage_watcher for watcher indices', async () => { - callCluster.mockResolvedValueOnce({ has_all_requested: true }); + clusterClient.asCurrentUser.security.hasPrivileges.mockResolvedValueOnce( + asApiResponse({ + has_all_requested: true, + }) + ); await service.hasRequiredPrivileges('.watches'); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_security/user/_has_privileges', - method: 'POST', + expect(clusterClient.asCurrentUser.security.hasPrivileges).toHaveBeenCalledWith({ body: { cluster: ['manage', 'manage_watcher'], index: [ @@ -212,7 +227,7 @@ describe('reindexService', () => { describe('createReindexOperation', () => { it('creates new reindex operation', async () => { - callCluster.mockResolvedValueOnce(true); // indices.exist + clusterClient.asCurrentUser.indices.exists.mockResolvedValueOnce(asApiResponse(true)); actions.findReindexOperations.mockResolvedValueOnce({ total: 0 }); actions.createReindexOp.mockResolvedValueOnce(); @@ -222,13 +237,13 @@ describe('reindexService', () => { }); it('fails if index does not exist', async () => { - callCluster.mockResolvedValueOnce(false); // indices.exist + clusterClient.asCurrentUser.indices.exists.mockResolvedValueOnce(asApiResponse(false)); await expect(service.createReindexOperation('myIndex')).rejects.toThrow(); expect(actions.createReindexOp).not.toHaveBeenCalled(); }); it('deletes existing operation if it failed', async () => { - callCluster.mockResolvedValueOnce(true); // indices.exist + clusterClient.asCurrentUser.indices.exists.mockResolvedValueOnce(asApiResponse(true)); actions.findReindexOperations.mockResolvedValueOnce({ saved_objects: [{ id: 1, attributes: { status: ReindexStatus.failed } }], total: 1, @@ -244,7 +259,7 @@ describe('reindexService', () => { }); it('deletes existing operation if it was cancelled', async () => { - callCluster.mockResolvedValueOnce(true); // indices.exist + clusterClient.asCurrentUser.indices.exists.mockResolvedValueOnce(asApiResponse(true)); actions.findReindexOperations.mockResolvedValueOnce({ saved_objects: [{ id: 1, attributes: { status: ReindexStatus.cancelled } }], total: 1, @@ -260,7 +275,7 @@ describe('reindexService', () => { }); it('fails if existing operation did not fail', async () => { - callCluster.mockResolvedValueOnce(true); // indices.exist + clusterClient.asCurrentUser.indices.exists.mockResolvedValueOnce(asApiResponse(true)); actions.findReindexOperations.mockResolvedValueOnce({ saved_objects: [{ id: 1, attributes: { status: ReindexStatus.inProgress } }], total: 1, @@ -418,10 +433,11 @@ describe('reindexService', () => { reindexTaskId: '999333', }, } as any); - callCluster.mockResolvedValueOnce(true); + + clusterClient.asCurrentUser.tasks.cancel.mockResolvedValueOnce(asApiResponse(true)); await service.cancelReindexing('myIndex'); - expect(callCluster).toHaveBeenCalledWith('tasks.cancel', { taskId: '999333' }); + expect(clusterClient.asCurrentUser.tasks.cancel).toHaveBeenCalledWith({ task_id: '999333' }); findSpy.mockRestore(); }); @@ -433,7 +449,11 @@ describe('reindexService', () => { const findSpy = jest.spyOn(service, 'findReindexOperation').mockResolvedValueOnce(reindexOp); await expect(service.cancelReindexing('myIndex')).rejects.toThrow(); - expect(callCluster).not.toHaveBeenCalledWith('tasks.cancel', { taskId: '999333' }); + expect(clusterClient.asCurrentUser.tasks.cancel).not.toHaveBeenCalledWith( + asApiResponse({ + taskId: '999333', + }) + ); findSpy.mockRestore(); }); @@ -450,7 +470,11 @@ describe('reindexService', () => { const findSpy = jest.spyOn(service, 'findReindexOperation').mockResolvedValueOnce(reindexOp); await expect(service.cancelReindexing('myIndex')).rejects.toThrow(); - expect(callCluster).not.toHaveBeenCalledWith('tasks.cancel', { taskId: '999333' }); + expect(clusterClient.asCurrentUser.tasks.cancel).not.toHaveBeenCalledWith( + asApiResponse({ + taskId: '999333', + }) + ); findSpy.mockRestore(); }); @@ -525,7 +549,7 @@ describe('reindexService', () => { ); expect(actions.incrementIndexGroupReindexes).not.toHaveBeenCalled(); expect(actions.runWhileIndexGroupLocked).not.toHaveBeenCalled(); - expect(callCluster).not.toHaveBeenCalled(); + expect(clusterClient.asCurrentUser.nodes.info).not.toHaveBeenCalled(); }); it('supports an already migrated ML index', async () => { @@ -533,11 +557,12 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f() ); - callCluster - // Mock call to /_nodes for version check - .mockResolvedValueOnce({ nodes: { nodeX: { version: '6.7.0-alpha' } } }) - // Mock call to /_ml/set_upgrade_mode?enabled=true - .mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.nodes.info.mockResolvedValueOnce( + asApiResponse({ nodes: { nodeX: { version: '6.7.0-alpha' } } }) + ); + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const mlReindexedOp = { id: '2', @@ -550,9 +575,8 @@ describe('reindexService', () => { ); expect(actions.incrementIndexGroupReindexes).toHaveBeenCalled(); expect(actions.runWhileIndexGroupLocked).toHaveBeenCalled(); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).toHaveBeenCalledWith({ + enabled: true, }); }); @@ -561,11 +585,13 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f() ); - callCluster - // Mock call to /_nodes for version check - .mockResolvedValueOnce({ nodes: { nodeX: { version: '6.7.0-alpha' } } }) - // Mock call to /_ml/set_upgrade_mode?enabled=true - .mockResolvedValueOnce({ acknowledged: true }); + + clusterClient.asCurrentUser.nodes.info.mockResolvedValueOnce( + asApiResponse({ nodes: { nodeX: { version: '6.7.0-alpha' } } }) + ); + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( @@ -573,9 +599,8 @@ describe('reindexService', () => { ); expect(actions.incrementIndexGroupReindexes).toHaveBeenCalled(); expect(actions.runWhileIndexGroupLocked).toHaveBeenCalled(); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).toHaveBeenCalledWith({ + enabled: true, }); }); @@ -587,9 +612,8 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: true, }); }); @@ -602,9 +626,8 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: true, }); }); @@ -613,11 +636,12 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f() ); - callCluster - // Mock call to /_nodes for version check - .mockResolvedValueOnce({ nodes: { nodeX: { version: '6.7.0' } } }) - // Mock call to /_ml/set_upgrade_mode?enabled=true - .mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.nodes.info.mockResolvedValueOnce( + asApiResponse({ nodes: { nodeX: { version: '6.7.0' } } }) + ); + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.created); @@ -626,9 +650,8 @@ describe('reindexService', () => { updatedOp.attributes.errorMessage!.includes('Could not stop ML jobs') ).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).toHaveBeenCalledWith({ + enabled: true, }); }); @@ -637,9 +660,9 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f() ); - callCluster - // Mock call to /_nodes for version check - .mockResolvedValueOnce({ nodes: { nodeX: { version: '6.6.0' } } }); + clusterClient.asCurrentUser.nodes.info.mockResolvedValueOnce( + asApiResponse({ nodes: { nodeX: { version: '6.6.0' } } }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.created); @@ -649,9 +672,8 @@ describe('reindexService', () => { ).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); // Should not have called ML endpoint at all - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: true, }); }); }); @@ -669,7 +691,7 @@ describe('reindexService', () => { ); expect(actions.incrementIndexGroupReindexes).not.toHaveBeenCalled(); expect(actions.runWhileIndexGroupLocked).not.toHaveBeenCalled(); - expect(callCluster).not.toHaveBeenCalled(); + expect(clusterClient.asCurrentUser.watcher.stop).not.toHaveBeenCalled(); }); it('increments ML reindexes and calls watcher stop endpoint', async () => { @@ -677,9 +699,9 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (type: string, f: any) => f() ); - callCluster - // Mock call to /_watcher/_stop - .mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.watcher.stop.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(watcherReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( @@ -687,10 +709,7 @@ describe('reindexService', () => { ); expect(actions.incrementIndexGroupReindexes).toHaveBeenCalledWith(IndexGroup.watcher); expect(actions.runWhileIndexGroupLocked).toHaveBeenCalled(); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_stop', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.stop).toHaveBeenCalled(); }); it('fails if watcher reindexes cannot be incremented', async () => { @@ -701,9 +720,8 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_stop', - method: 'POST', + expect(clusterClient.asCurrentUser.watcher.stop).not.toHaveBeenCalledWith({ + enabled: true, }); }); @@ -716,10 +734,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_stop', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.stop).not.toHaveBeenCalled(); }); it('fails if watcher endpoint fails', async () => { @@ -727,9 +742,9 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (type: string, f: any) => f() ); - callCluster - // Mock call to /_watcher/_stop - .mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.watcher.stop.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(watcherReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.created); @@ -738,10 +753,7 @@ describe('reindexService', () => { updatedOp.attributes.errorMessage!.includes('Could not stop Watcher') ).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_stop', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.stop).toHaveBeenCalled(); }); }); }); @@ -756,17 +768,21 @@ describe('reindexService', () => { } as ReindexSavedObject; it('blocks writes and updates lastCompletedStep', async () => { - callCluster.mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.indices.putSettings.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.readonly); - expect(callCluster).toHaveBeenCalledWith('indices.putSettings', { + expect(clusterClient.asCurrentUser.indices.putSettings).toHaveBeenCalledWith({ index: 'myIndex', body: { 'index.blocks.write': true }, }); }); it('fails if setting updates are not acknowledged', async () => { - callCluster.mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.indices.putSettings.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStopped @@ -777,7 +793,7 @@ describe('reindexService', () => { }); it('fails if setting updates fail', async () => { - callCluster.mockRejectedValueOnce(new Error('blah!')); + clusterClient.asCurrentUser.indices.putSettings.mockRejectedValueOnce(new Error('blah!')); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStopped @@ -797,11 +813,12 @@ describe('reindexService', () => { // The more intricate details of how the settings are chosen are test separately. it('creates new index with settings and mappings and updates lastCompletedStep', async () => { actions.getFlatSettings.mockResolvedValueOnce(settingsMappings); - callCluster.mockResolvedValueOnce({ acknowledged: true }); // indices.create - + clusterClient.asCurrentUser.indices.create.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.newIndexCreated); - expect(callCluster).toHaveBeenCalledWith('indices.create', { + expect(clusterClient.asCurrentUser.indices.create).toHaveBeenCalledWith({ index: 'myIndex-reindex-0', body: { // index.blocks.write should be removed from the settings for the new index. @@ -812,9 +829,13 @@ describe('reindexService', () => { }); it('fails if create index is not acknowledged', async () => { - callCluster - .mockResolvedValueOnce({ myIndex: settingsMappings }) - .mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.indices.getSettings.mockResolvedValueOnce( + asApiResponse({ myIndex: settingsMappings }) + ); + + clusterClient.asCurrentUser.indices.create.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.readonly); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -823,10 +844,16 @@ describe('reindexService', () => { }); it('fails if create index fails', async () => { - callCluster - .mockResolvedValueOnce({ myIndex: settingsMappings }) - .mockRejectedValueOnce(new Error(`blah!`)) - .mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.indices.getSettings.mockResolvedValueOnce( + asApiResponse({ myIndex: settingsMappings }) + ); + + clusterClient.asCurrentUser.indices.create.mockRejectedValueOnce(new Error(`blah!`)); + + clusterClient.asCurrentUser.indices.putSettings.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); + const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.readonly); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -834,7 +861,7 @@ describe('reindexService', () => { expect(log.error).toHaveBeenCalledWith(expect.any(String)); // Original index should have been set back to allow reads. - expect(callCluster).toHaveBeenCalledWith('indices.putSettings', { + expect(clusterClient.asCurrentUser.indices.putSettings).toHaveBeenCalledWith({ index: 'myIndex', body: { 'index.blocks.write': false }, }); @@ -858,14 +885,14 @@ describe('reindexService', () => { }); it('starts reindex, saves taskId, and updates lastCompletedStep', async () => { - callCluster.mockResolvedValueOnce({ task: 'xyz' }); // reindex + clusterClient.asCurrentUser.reindex.mockResolvedValueOnce(asApiResponse({ task: 'xyz' })); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexStarted); expect(updatedOp.attributes.reindexTaskId).toEqual('xyz'); expect(updatedOp.attributes.reindexTaskPercComplete).toEqual(0); - expect(callCluster).toHaveBeenLastCalledWith('reindex', { + expect(clusterClient.asCurrentUser.reindex).toHaveBeenLastCalledWith({ refresh: true, - waitForCompletion: false, + wait_for_completion: false, body: { source: { index: 'myIndex' }, dest: { index: 'myIndex-reindex-0' }, @@ -874,7 +901,7 @@ describe('reindexService', () => { }); it('fails if starting reindex fails', async () => { - callCluster.mockRejectedValueOnce(new Error('blah!')).mockResolvedValueOnce({}); + clusterClient.asCurrentUser.reindex.mockRejectedValueOnce(new Error('blah!')); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.newIndexCreated); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -895,10 +922,13 @@ describe('reindexService', () => { describe('reindex task is not complete', () => { it('updates reindexTaskPercComplete', async () => { - callCluster.mockResolvedValueOnce({ - completed: false, - task: { status: { created: 10, total: 100 } }, - }); + clusterClient.asCurrentUser.tasks.get.mockResolvedValueOnce( + asApiResponse({ + completed: false, + task: { status: { created: 10, total: 100 } }, + }) + ); + const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexStarted); expect(updatedOp.attributes.reindexTaskPercComplete).toEqual(0.1); // 10 / 100 = 0.1 @@ -907,30 +937,47 @@ describe('reindexService', () => { describe('reindex task is complete', () => { it('deletes task, updates reindexTaskPercComplete, updates lastCompletedStep', async () => { - callCluster - .mockResolvedValueOnce({ + clusterClient.asCurrentUser.tasks.get.mockResolvedValueOnce( + asApiResponse({ completed: true, task: { status: { created: 100, total: 100 } }, }) - .mockResolvedValueOnce({ count: 100 }) - .mockResolvedValueOnce({ result: 'deleted' }); + ); + + clusterClient.asCurrentUser.count.mockResolvedValueOnce( + asApiResponse({ + count: 100, + }) + ); + + clusterClient.asCurrentUser.delete.mockResolvedValueOnce( + asApiResponse({ + result: 'deleted', + }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexCompleted); expect(updatedOp.attributes.reindexTaskPercComplete).toEqual(1); - expect(callCluster).toHaveBeenCalledWith('delete', { + expect(clusterClient.asCurrentUser.delete).toHaveBeenCalledWith({ index: '.tasks', id: 'xyz', }); }); it('fails if docs created is less than count in source index', async () => { - callCluster - .mockResolvedValueOnce({ + clusterClient.asCurrentUser.tasks.get.mockResolvedValueOnce( + asApiResponse({ completed: true, task: { status: { created: 95, total: 95 } }, }) - .mockReturnValueOnce({ count: 100 }); + ); + + clusterClient.asCurrentUser.count.mockResolvedValueOnce( + asApiResponse({ + count: 100, + }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexStarted); @@ -941,18 +988,22 @@ describe('reindexService', () => { }); describe('reindex task is cancelled', () => { - it('deletes tsk, updates status to cancelled', async () => { - callCluster - .mockResolvedValueOnce({ + it('deletes task, updates status to cancelled', async () => { + clusterClient.asCurrentUser.tasks.get.mockResolvedValueOnce( + asApiResponse({ completed: true, task: { status: { created: 100, total: 100, canceled: 'by user request' } }, }) - .mockResolvedValue({ result: 'deleted' }); + ); + + clusterClient.asCurrentUser.delete.mockResolvedValue( + asApiResponse({ result: 'deleted' }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexStarted); expect(updatedOp.attributes.status).toEqual(ReindexStatus.cancelled); - expect(callCluster).toHaveBeenCalledWith('delete', { + expect(clusterClient.asCurrentUser.delete).toHaveBeenLastCalledWith({ index: '.tasks', id: 'xyz', }); @@ -971,12 +1022,16 @@ describe('reindexService', () => { } as ReindexSavedObject; it('switches aliases, sets as complete, and updates lastCompletedStep', async () => { - callCluster - .mockResolvedValueOnce({ myIndex: { aliases: {} } }) - .mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.indices.getAlias.mockResolvedValue( + asApiResponse({ myIndex: { aliases: {} } }) + ); + + clusterClient.asCurrentUser.indices.updateAliases.mockResolvedValue( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); - expect(callCluster).toHaveBeenCalledWith('indices.updateAliases', { + expect(clusterClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ body: { actions: [ { add: { index: 'myIndex-reindex-0', alias: 'myIndex' } }, @@ -987,8 +1042,8 @@ describe('reindexService', () => { }); it('moves existing aliases over to new index', async () => { - callCluster - .mockResolvedValueOnce({ + clusterClient.asCurrentUser.indices.getAlias.mockResolvedValue( + asApiResponse({ myIndex: { aliases: { myAlias: {}, @@ -996,10 +1051,15 @@ describe('reindexService', () => { }, }, }) - .mockResolvedValueOnce({ acknowledged: true }); + ); + + clusterClient.asCurrentUser.indices.updateAliases.mockResolvedValue( + asApiResponse({ acknowledged: true }) + ); + const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); - expect(callCluster).toHaveBeenCalledWith('indices.updateAliases', { + expect(clusterClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ body: { actions: [ { add: { index: 'myIndex-reindex-0', alias: 'myIndex' } }, @@ -1018,7 +1078,9 @@ describe('reindexService', () => { }); it('fails if switching aliases is not acknowledged', async () => { - callCluster.mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.indices.updateAliases.mockResolvedValue( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexCompleted); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -1027,7 +1089,7 @@ describe('reindexService', () => { }); it('fails if switching aliases fails', async () => { - callCluster.mockRejectedValueOnce(new Error('blah!')); + clusterClient.asCurrentUser.indices.updateAliases.mockRejectedValueOnce(new Error('blah!')); const updatedOp = await service.processNextStep(reindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.reindexCompleted); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -1053,7 +1115,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).not.toHaveBeenCalled(); + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalled(); }); it('decrements ML reindexes and calls ML start endpoint if no remaining ML jobs', async () => { @@ -1061,17 +1123,17 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 0 } }) ); - // Mock call to /_ml/set_upgrade_mode?enabled=false - callCluster.mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(actions.decrementIndexGroupReindexes).toHaveBeenCalledWith(IndexGroup.ml); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).toHaveBeenCalledWith({ + enabled: false, }); }); @@ -1080,16 +1142,16 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 2 } }) ); - // Mock call to /_ml/set_upgrade_mode?enabled=false - callCluster.mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: false, }); }); @@ -1102,9 +1164,8 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: false, }); }); @@ -1118,9 +1179,8 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).not.toHaveBeenCalledWith({ + enabled: false, }); }); @@ -1129,9 +1189,9 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 0 } }) ); - // Mock call to /_ml/set_upgrade_mode?enabled=true - callCluster.mockResolvedValueOnce({ acknowledged: false }); - + clusterClient.asCurrentUser.ml.setUpgradeMode.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(mlReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -1139,9 +1199,8 @@ describe('reindexService', () => { updatedOp.attributes.errorMessage!.includes('Could not resume ML jobs') ).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + expect(clusterClient.asCurrentUser.ml.setUpgradeMode).toHaveBeenCalledWith({ + enabled: false, }); }); }); @@ -1157,7 +1216,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).not.toHaveBeenCalled(); + expect(clusterClient.asCurrentUser.watcher.start).not.toHaveBeenCalled(); }); it('decrements watcher reindexes and calls wathcer start endpoint if no remaining watcher reindexes', async () => { @@ -1165,36 +1224,31 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 0 } }) ); - // Mock call to /_watcher/_start - callCluster.mockResolvedValueOnce({ acknowledged: true }); + clusterClient.asCurrentUser.watcher.start.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(watcherReindexOp); expect(actions.decrementIndexGroupReindexes).toHaveBeenCalledWith(IndexGroup.watcher); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.start).toHaveBeenCalled(); }); - it('does not call wathcer start endpoint if there are remaining wathcer reindexes', async () => { + it('does not call watcher start endpoint if there are remaining watcher reindexes', async () => { actions.decrementIndexGroupReindexes.mockResolvedValue(); actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 2 } }) ); - // Mock call to /_watcher/_start - callCluster.mockResolvedValueOnce({ acknowledged: true }); - + clusterClient.asCurrentUser.watcher.start.mockResolvedValueOnce( + asApiResponse({ acknowledged: true }) + ); const updatedOp = await service.processNextStep(watcherReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual( ReindexStep.indexGroupServicesStarted ); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.start).not.toHaveBeenCalledWith(); }); it('fails if watcher reindexes cannot be decremented', async () => { @@ -1206,10 +1260,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.start).not.toHaveBeenCalledWith(); }); it('fails if watcher doc cannot be locked', async () => { @@ -1222,10 +1273,7 @@ describe('reindexService', () => { expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); expect(updatedOp.attributes.errorMessage!.includes(`Can't lock!`)).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).not.toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.start).not.toHaveBeenCalledWith(); }); it('fails if watcher endpoint fails', async () => { @@ -1233,9 +1281,10 @@ describe('reindexService', () => { actions.runWhileIndexGroupLocked.mockImplementationOnce(async (group: string, f: any) => f({ attributes: { runningReindexCount: 0 } }) ); - // Mock call to /_watcher/_start - callCluster.mockResolvedValueOnce({ acknowledged: false }); + clusterClient.asCurrentUser.watcher.start.mockResolvedValueOnce( + asApiResponse({ acknowledged: false }) + ); const updatedOp = await service.processNextStep(watcherReindexOp); expect(updatedOp.attributes.lastCompletedStep).toEqual(ReindexStep.aliasCreated); expect(updatedOp.attributes.status).toEqual(ReindexStatus.failed); @@ -1243,10 +1292,7 @@ describe('reindexService', () => { updatedOp.attributes.errorMessage!.includes('Could not start Watcher') ).toBeTruthy(); expect(log.error).toHaveBeenCalledWith(expect.any(String)); - expect(callCluster).toHaveBeenCalledWith('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + expect(clusterClient.asCurrentUser.watcher.start).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts index e784f42867d57..f59dc66f40612 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/reindex_service.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { LegacyAPICaller, Logger } from 'src/core/server'; +import { ElasticsearchClient, Logger } from 'src/core/server'; import { first } from 'rxjs/operators'; import { LicensingPluginSetup } from '../../../../licensing/server'; @@ -127,7 +127,7 @@ export interface ReindexService { } export const reindexServiceFactory = ( - callAsUser: LegacyAPICaller, + esClient: ElasticsearchClient, actions: ReindexActions, log: Logger, licensing: LicensingPluginSetup @@ -144,12 +144,11 @@ export const reindexServiceFactory = ( await actions.runWhileIndexGroupLocked(IndexGroup.ml, async (mlDoc) => { await validateNodesMinimumVersion(6, 7); - const res = await callAsUser('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=true', - method: 'POST', + const { body } = await esClient.ml.setUpgradeMode({ + enabled: true, }); - if (!res.acknowledged) { + if (!body.acknowledged) { throw new Error(`Could not stop ML jobs`); } @@ -164,12 +163,11 @@ export const reindexServiceFactory = ( await actions.decrementIndexGroupReindexes(IndexGroup.ml); await actions.runWhileIndexGroupLocked(IndexGroup.ml, async (mlDoc) => { if (mlDoc.attributes.runningReindexCount === 0) { - const res = await callAsUser('transport.request', { - path: '/_ml/set_upgrade_mode?enabled=false', - method: 'POST', + const { body } = await esClient.ml.setUpgradeMode({ + enabled: false, }); - if (!res.acknowledged) { + if (!body.acknowledged) { throw new Error(`Could not resume ML jobs`); } } @@ -184,12 +182,9 @@ export const reindexServiceFactory = ( const stopWatcher = async () => { await actions.incrementIndexGroupReindexes(IndexGroup.watcher); await actions.runWhileIndexGroupLocked(IndexGroup.watcher, async (watcherDoc) => { - const { acknowledged } = await callAsUser('transport.request', { - path: '/_watcher/_stop', - method: 'POST', - }); + const { body } = await esClient.watcher.stop(); - if (!acknowledged) { + if (!body.acknowledged) { throw new Error('Could not stop Watcher'); } @@ -204,12 +199,9 @@ export const reindexServiceFactory = ( await actions.decrementIndexGroupReindexes(IndexGroup.watcher); await actions.runWhileIndexGroupLocked(IndexGroup.watcher, async (watcherDoc) => { if (watcherDoc.attributes.runningReindexCount === 0) { - const { acknowledged } = await callAsUser('transport.request', { - path: '/_watcher/_start', - method: 'POST', - }); + const { body } = await esClient.watcher.start(); - if (!acknowledged) { + if (!body.acknowledged) { throw new Error('Could not start Watcher'); } } @@ -221,14 +213,16 @@ export const reindexServiceFactory = ( const cleanupChanges = async (reindexOp: ReindexSavedObject) => { // Cancel reindex task if it was started but not completed if (reindexOp.attributes.lastCompletedStep === ReindexStep.reindexStarted) { - await callAsUser('tasks.cancel', { - taskId: reindexOp.attributes.reindexTaskId, - }).catch((e) => undefined); // Ignore any exceptions trying to cancel (it may have already completed). + await esClient.tasks + .cancel({ + task_id: reindexOp.attributes.reindexTaskId ?? undefined, + }) + .catch((e) => undefined); // Ignore any exceptions trying to cancel (it may have already completed). } // Set index back to writable if we ever got past this point. if (reindexOp.attributes.lastCompletedStep >= ReindexStep.readonly) { - await callAsUser('indices.putSettings', { + await esClient.indices.putSettings({ index: reindexOp.attributes.indexName, body: { 'index.blocks.write': false }, }); @@ -238,7 +232,9 @@ export const reindexServiceFactory = ( reindexOp.attributes.lastCompletedStep >= ReindexStep.newIndexCreated && reindexOp.attributes.lastCompletedStep < ReindexStep.aliasCreated ) { - await callAsUser('indices.delete', { index: reindexOp.attributes.newIndexName }); + await esClient.indices.delete({ + index: reindexOp.attributes.newIndexName, + }); } // Resume consumers if we ever got past this point. @@ -252,10 +248,7 @@ export const reindexServiceFactory = ( // ------ Functions used to process the state machine const validateNodesMinimumVersion = async (minMajor: number, minMinor: number) => { - const nodesResponse = await callAsUser('transport.request', { - path: '/_nodes', - method: 'GET', - }); + const { body: nodesResponse } = await esClient.nodes.info(); const outDatedNodes = Object.values(nodesResponse.nodes).filter((node: any) => { const matches = node.version.match(VERSION_REGEX); @@ -293,7 +286,7 @@ export const reindexServiceFactory = ( */ const setReadonly = async (reindexOp: ReindexSavedObject) => { const { indexName } = reindexOp.attributes; - const putReadonly = await callAsUser('indices.putSettings', { + const { body: putReadonly } = await esClient.indices.putSettings({ index: indexName, body: { 'index.blocks.write': true }, }); @@ -319,7 +312,7 @@ export const reindexServiceFactory = ( const { settings, mappings } = transformFlatSettings(flatSettings); - const createIndex = await callAsUser('indices.create', { + const { body: createIndex } = await esClient.indices.create({ index: newIndexName, body: { settings, @@ -345,25 +338,25 @@ export const reindexServiceFactory = ( // Where possible, derive reindex options at the last moment before reindexing // to prevent them from becoming stale as they wait in the queue. - const indicesState = await esIndicesStateCheck(callAsUser, [indexName]); + const indicesState = await esIndicesStateCheck(esClient, [indexName]); const shouldOpenAndClose = indicesState[indexName] === 'closed'; if (shouldOpenAndClose) { log.debug(`Detected closed index ${indexName}, opening...`); - await callAsUser('indices.open', { index: indexName }); + await esClient.indices.open({ index: indexName }); } - const startReindex = (await callAsUser('reindex', { + const { body: startReindexResponse } = await esClient.reindex({ refresh: true, - waitForCompletion: false, + wait_for_completion: false, body: { source: { index: indexName }, dest: { index: reindexOp.attributes.newIndexName }, }, - })) as any; + }); return actions.updateReindexOp(reindexOp, { lastCompletedStep: ReindexStep.reindexStarted, - reindexTaskId: startReindex.task, + reindexTaskId: startReindexResponse.task, reindexTaskPercComplete: 0, reindexOptions: { ...(reindexOptions ?? {}), @@ -379,12 +372,12 @@ export const reindexServiceFactory = ( * @param reindexOp */ const updateReindexStatus = async (reindexOp: ReindexSavedObject) => { - const taskId = reindexOp.attributes.reindexTaskId; + const taskId = reindexOp.attributes.reindexTaskId!; // Check reindexing task progress - const taskResponse = await callAsUser('tasks.get', { - taskId, - waitForCompletion: false, + const { body: taskResponse } = await esClient.tasks.get({ + task_id: taskId, + wait_for_completion: false, }); if (!taskResponse.completed) { @@ -403,7 +396,7 @@ export const reindexServiceFactory = ( reindexOp = await cleanupChanges(reindexOp); } else { // Check that it reindexed all documents - const { count } = await callAsUser('count', { index: reindexOp.attributes.indexName }); + const { body: count } = await esClient.count({ index: reindexOp.attributes.indexName }); if (taskResponse.task.status.created < count) { // Include the entire task result in the error message. This should be guaranteed @@ -419,7 +412,7 @@ export const reindexServiceFactory = ( } // Delete the task from ES .tasks index - const deleteTaskResp = await callAsUser('delete', { + const { body: deleteTaskResp } = await esClient.delete({ index: '.tasks', id: taskId, }); @@ -438,22 +431,22 @@ export const reindexServiceFactory = ( const switchAlias = async (reindexOp: ReindexSavedObject) => { const { indexName, newIndexName, reindexOptions } = reindexOp.attributes; - const existingAliases = ( - await callAsUser('indices.getAlias', { - index: indexName, - }) - )[indexName].aliases; + const { body: response } = await esClient.indices.getAlias({ + index: indexName, + }); + + const existingAliases = response[indexName].aliases; - const extraAlises = Object.keys(existingAliases).map((aliasName) => ({ + const extraAliases = Object.keys(existingAliases).map((aliasName) => ({ add: { index: newIndexName, alias: aliasName, ...existingAliases[aliasName] }, })); - const aliasResponse = await callAsUser('indices.updateAliases', { + const { body: aliasResponse } = await esClient.indices.updateAliases({ body: { actions: [ { add: { index: newIndexName, alias: indexName } }, { remove_index: { index: indexName } }, - ...extraAlises, + ...extraAliases, ], }, }); @@ -463,7 +456,7 @@ export const reindexServiceFactory = ( } if (reindexOptions?.openAndClose === true) { - await callAsUser('indices.close', { index: indexName }); + await esClient.indices.close({ index: indexName }); } return actions.updateReindexOp(reindexOp, { @@ -540,9 +533,7 @@ export const reindexServiceFactory = ( body.cluster = [...body.cluster, 'manage_watcher']; } - const resp = await callAsUser('transport.request', { - path: '/_security/user/_has_privileges', - method: 'POST', + const { body: resp } = await esClient.security.hasPrivileges({ body, }); @@ -567,7 +558,7 @@ export const reindexServiceFactory = ( }, async createReindexOperation(indexName: string, opts?: { enqueue: boolean }) { - const indexExists = await callAsUser('indices.exists', { index: indexName }); + const { body: indexExists } = await esClient.indices.exists({ index: indexName }); if (!indexExists) { throw error.indexNotFound(`Index ${indexName} does not exist in this cluster.`); } @@ -752,8 +743,8 @@ export const reindexServiceFactory = ( ); } - const resp = await callAsUser('tasks.cancel', { - taskId: reindexOp.attributes.reindexTaskId, + const { body: resp } = await esClient.tasks.cancel({ + task_id: reindexOp.attributes.reindexTaskId!, }); if (resp.node_failures && resp.node_failures.length > 0) { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/worker.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/worker.ts index 756ce17b60199..1a30f98296c3a 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/worker.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/worker.ts @@ -3,12 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { - ILegacyClusterClient, - Logger, - SavedObjectsClientContract, - FakeRequest, -} from 'src/core/server'; +import { IClusterClient, Logger, SavedObjectsClientContract, FakeRequest } from 'src/core/server'; import moment from 'moment'; import { ReindexSavedObject, ReindexStatus } from '../../../common/types'; import { Credential, CredentialStore } from './credential_store'; @@ -53,7 +48,7 @@ export class ReindexWorker { constructor( private client: SavedObjectsClientContract, private credentialStore: CredentialStore, - private clusterClient: ILegacyClusterClient, + private clusterClient: IClusterClient, log: Logger, private licensing: LicensingPluginSetup ) { @@ -62,7 +57,7 @@ export class ReindexWorker { throw new Error(`More than one ReindexWorker cannot be created.`); } - const callAsInternalUser = this.clusterClient.callAsInternalUser.bind(this.clusterClient); + const callAsInternalUser = this.clusterClient.asInternalUser; this.reindexService = reindexServiceFactory( callAsInternalUser, @@ -151,7 +146,7 @@ export class ReindexWorker { private getCredentialScopedReindexService = (credential: Credential) => { const fakeRequest: FakeRequest = { headers: credential }; const scopedClusterClient = this.clusterClient.asScoped(fakeRequest); - const callAsCurrentUser = scopedClusterClient.callAsCurrentUser.bind(scopedClusterClient); + const callAsCurrentUser = scopedClusterClient.asCurrentUser; const actions = reindexActionsFactory(this.client, callAsCurrentUser); return reindexServiceFactory(callAsCurrentUser, actions, this.log, this.licensing); }; diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts index e14056439ca6b..9b5223d07bfbf 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.test.ts @@ -5,7 +5,7 @@ */ import { elasticsearchServiceMock } from 'src/core/server/mocks'; import { registerUpgradeAssistantUsageCollector } from './usage_collector'; -import { ILegacyClusterClient } from 'src/core/server'; +import { IClusterClient } from 'src/core/server'; /** * Since these route callbacks are so thin, these serve simply as integration tests @@ -18,15 +18,17 @@ describe('Upgrade Assistant Usage Collector', () => { let dependencies: any; let callClusterStub: any; let usageCollection: any; - let clusterClient: ILegacyClusterClient; + let clusterClient: IClusterClient; beforeEach(() => { - clusterClient = elasticsearchServiceMock.createLegacyClusterClient(); - (clusterClient.callAsInternalUser as jest.Mock).mockResolvedValue({ - persistent: {}, - transient: { - logger: { - deprecation: 'WARN', + clusterClient = elasticsearchServiceMock.createClusterClient(); + (clusterClient.asInternalUser.cluster.getSettings as jest.Mock).mockResolvedValue({ + body: { + persistent: {}, + transient: { + logger: { + deprecation: 'WARN', + }, }, }, }); @@ -59,7 +61,7 @@ describe('Upgrade Assistant Usage Collector', () => { }), }, elasticsearch: { - legacy: { client: clusterClient }, + client: clusterClient, }, }; }); diff --git a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts index 276e639678fd8..1eafefb4238c2 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/telemetry/usage_collector.ts @@ -6,7 +6,7 @@ import { get } from 'lodash'; import { - LegacyAPICaller, + ElasticsearchClient, ElasticsearchServiceStart, ISavedObjectsRepository, SavedObjectsServiceStart, @@ -38,12 +38,10 @@ async function getSavedObjectAttributesFromRepo( } } -async function getDeprecationLoggingStatusValue( - callAsCurrentUser: LegacyAPICaller -): Promise { +async function getDeprecationLoggingStatusValue(esClient: ElasticsearchClient): Promise { try { - const loggerDeprecationCallResult = await callAsCurrentUser('cluster.getSettings', { - includeDefaults: true, + const { body: loggerDeprecationCallResult } = await esClient.cluster.getSettings({ + include_defaults: true, }); return isDeprecationLoggingEnabled(loggerDeprecationCallResult); @@ -53,7 +51,7 @@ async function getDeprecationLoggingStatusValue( } export async function fetchUpgradeAssistantMetrics( - { legacy: { client: esClient } }: ElasticsearchServiceStart, + { client: esClient }: ElasticsearchServiceStart, savedObjects: SavedObjectsServiceStart ): Promise { const savedObjectsRepository = savedObjects.createInternalRepository(); @@ -62,8 +60,9 @@ export async function fetchUpgradeAssistantMetrics( UPGRADE_ASSISTANT_TYPE, UPGRADE_ASSISTANT_DOC_ID ); - const callAsInternalUser = esClient.callAsInternalUser.bind(esClient); - const deprecationLoggingStatusValue = await getDeprecationLoggingStatusValue(callAsInternalUser); + const deprecationLoggingStatusValue = await getDeprecationLoggingStatusValue( + esClient.asInternalUser + ); const getTelemetrySavedObject = ( upgradeAssistantTelemetrySavedObjectAttrs: UpgradeAssistantTelemetrySavedObjectAttributes | null diff --git a/x-pack/plugins/upgrade_assistant/server/routes/__mocks__/routes.mock.ts b/x-pack/plugins/upgrade_assistant/server/routes/__mocks__/routes.mock.ts index 2df770c3ce45c..43fe8af18c392 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/__mocks__/routes.mock.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/__mocks__/routes.mock.ts @@ -12,9 +12,7 @@ import { export const routeHandlerContextMock = ({ core: { elasticsearch: { - legacy: { - client: elasticsearchServiceMock.createLegacyScopedClusterClient(), - }, + client: elasticsearchServiceMock.createScopedClusterClient(), }, savedObjects: { client: savedObjectsClientMock.create() }, }, diff --git a/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts index 16f8001f8e1de..630ee9157dc90 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.test.ts @@ -80,7 +80,7 @@ describe('cluster checkup API', () => { it('returns an 403 error if it throws forbidden', async () => { const e: any = new Error(`you can't go here!`); - e.status = 403; + e.statusCode = 403; MigrationApis.getUpgradeAssistantStatus.mockRejectedValue(e); const resp = await routeDependencies.router.getHandler({ diff --git a/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.ts b/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.ts index ff673adaf1642..6eb0378b21baa 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/cluster_checkup.ts @@ -23,9 +23,7 @@ export function registerClusterCheckupRoutes({ cloud, router, licensing, log }: { core: { savedObjects: { client: savedObjectsClient }, - elasticsearch: { - legacy: { client }, - }, + elasticsearch: { client }, }, }, request, @@ -34,10 +32,10 @@ export function registerClusterCheckupRoutes({ cloud, router, licensing, log }: try { const status = await getUpgradeAssistantStatus(client, isCloudEnabled); - const callAsCurrentUser = client.callAsCurrentUser.bind(client); - const reindexActions = reindexActionsFactory(savedObjectsClient, callAsCurrentUser); + const asCurrentUser = client.asCurrentUser; + const reindexActions = reindexActionsFactory(savedObjectsClient, asCurrentUser); const reindexService = reindexServiceFactory( - callAsCurrentUser, + asCurrentUser, reindexActions, log, licensing @@ -52,7 +50,7 @@ export function registerClusterCheckupRoutes({ cloud, router, licensing, log }: body: status, }); } catch (e) { - if (e.status === 403) { + if (e.statusCode === 403) { return response.forbidden(e.message); } diff --git a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts index b8021eeef75e6..f76a86704e2c4 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.test.ts @@ -37,9 +37,9 @@ describe('deprecation logging API', () => { describe('GET /api/upgrade_assistant/deprecation_logging', () => { it('returns isEnabled', async () => { - (routeHandlerContextMock.core.elasticsearch.legacy.client - .callAsCurrentUser as jest.Mock).mockResolvedValue({ - default: { logger: { deprecation: 'WARN' } }, + (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster + .getSettings as jest.Mock).mockResolvedValue({ + body: { default: { logger: { deprecation: 'WARN' } } }, }); const resp = await routeDependencies.router.getHandler({ method: 'get', @@ -51,8 +51,8 @@ describe('deprecation logging API', () => { }); it('returns an error if it throws', async () => { - (routeHandlerContextMock.core.elasticsearch.legacy.client - .callAsCurrentUser as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster + .getSettings as jest.Mock).mockRejectedValue(new Error(`scary error!`)); const resp = await routeDependencies.router.getHandler({ method: 'get', pathPattern: '/api/upgrade_assistant/deprecation_logging', @@ -64,21 +64,21 @@ describe('deprecation logging API', () => { describe('PUT /api/upgrade_assistant/deprecation_logging', () => { it('returns isEnabled', async () => { - (routeHandlerContextMock.core.elasticsearch.legacy.client - .callAsCurrentUser as jest.Mock).mockResolvedValue({ - default: { logger: { deprecation: 'ERROR' } }, + (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster + .putSettings as jest.Mock).mockResolvedValue({ + body: { default: { logger: { deprecation: 'ERROR' } } }, }); const resp = await routeDependencies.router.getHandler({ - method: 'get', + method: 'put', pathPattern: '/api/upgrade_assistant/deprecation_logging', - })(routeHandlerContextMock, createRequestMock(), kibanaResponseFactory); + })(routeHandlerContextMock, { body: { isEnabled: true } }, kibanaResponseFactory); expect(resp.payload).toEqual({ isEnabled: false }); }); it('returns an error if it throws', async () => { - (routeHandlerContextMock.core.elasticsearch.legacy.client - .callAsCurrentUser as jest.Mock).mockRejectedValue(new Error(`scary error!`)); + (routeHandlerContextMock.core.elasticsearch.client.asCurrentUser.cluster + .putSettings as jest.Mock).mockRejectedValue(new Error(`scary error!`)); const resp = await routeDependencies.router.getHandler({ method: 'put', pathPattern: '/api/upgrade_assistant/deprecation_logging', diff --git a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.ts b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.ts index 38f244a4e1154..4bdb1364b071a 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/deprecation_logging.ts @@ -23,9 +23,7 @@ export function registerDeprecationLoggingRoutes({ router }: RouteDependencies) async ( { core: { - elasticsearch: { - legacy: { client }, - }, + elasticsearch: { client }, }, }, request, @@ -54,9 +52,7 @@ export function registerDeprecationLoggingRoutes({ router }: RouteDependencies) async ( { core: { - elasticsearch: { - legacy: { client }, - }, + elasticsearch: { client }, }, }, request, diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_handler.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_handler.ts index fffde339c59e5..e33398fdcc93b 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_handler.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_handler.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { ILegacyScopedClusterClient, Logger, SavedObjectsClientContract } from 'kibana/server'; +import { IScopedClusterClient, Logger, SavedObjectsClientContract } from 'kibana/server'; import { LicensingPluginSetup } from '../../../../licensing/server'; @@ -17,7 +17,7 @@ import { error } from '../../lib/reindexing/error'; interface ReindexHandlerArgs { savedObjects: SavedObjectsClientContract; - dataClient: ILegacyScopedClusterClient; + dataClient: IScopedClusterClient; indexName: string; log: Logger; licensing: LicensingPluginSetup; @@ -38,7 +38,7 @@ export const reindexHandler = async ({ savedObjects, reindexOptions, }: ReindexHandlerArgs): Promise => { - const callAsCurrentUser = dataClient.callAsCurrentUser.bind(dataClient); + const callAsCurrentUser = dataClient.asCurrentUser; const reindexActions = reindexActionsFactory(savedObjects, callAsCurrentUser); const reindexService = reindexServiceFactory(callAsCurrentUser, reindexActions, log, licensing); diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts index c745def5e8936..52157f39743e8 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts @@ -51,7 +51,7 @@ export function createReindexWorker({ savedObjects, licensing, }: CreateReindexWorker) { - const esClient = elasticsearchService.legacy.client; + const esClient = elasticsearchService.client; return new ReindexWorker(savedObjects, credentialStore, esClient, logger, licensing); } @@ -100,9 +100,7 @@ export function registerReindexIndicesRoutes( { core: { savedObjects: { client: savedObjectsClient }, - elasticsearch: { - legacy: { client: esClient }, - }, + elasticsearch: { client: esClient }, }, }, request, @@ -142,9 +140,7 @@ export function registerReindexIndicesRoutes( async ( { core: { - elasticsearch: { - legacy: { client: esClient }, - }, + elasticsearch: { client: esClient }, savedObjects, }, }, @@ -152,7 +148,7 @@ export function registerReindexIndicesRoutes( response ) => { const { client } = savedObjects; - const callAsCurrentUser = esClient.callAsCurrentUser.bind(esClient); + const callAsCurrentUser = esClient.asCurrentUser; const reindexActions = reindexActionsFactory(client, callAsCurrentUser); try { const inProgressOps = await reindexActions.findAllByStatus(ReindexStatus.inProgress); @@ -184,9 +180,7 @@ export function registerReindexIndicesRoutes( { core: { savedObjects: { client: savedObjectsClient }, - elasticsearch: { - legacy: { client: esClient }, - }, + elasticsearch: { client: esClient }, }, }, request, @@ -245,9 +239,7 @@ export function registerReindexIndicesRoutes( { core: { savedObjects, - elasticsearch: { - legacy: { client: esClient }, - }, + elasticsearch: { client: esClient }, }, }, request, @@ -255,14 +247,9 @@ export function registerReindexIndicesRoutes( ) => { const { client } = savedObjects; const { indexName } = request.params; - const callAsCurrentUser = esClient.callAsCurrentUser.bind(esClient); - const reindexActions = reindexActionsFactory(client, callAsCurrentUser); - const reindexService = reindexServiceFactory( - callAsCurrentUser, - reindexActions, - log, - licensing - ); + const asCurrentUser = esClient.asCurrentUser; + const reindexActions = reindexActionsFactory(client, asCurrentUser); + const reindexService = reindexServiceFactory(asCurrentUser, reindexActions, log, licensing); try { const hasRequiredPrivileges = await reindexService.hasRequiredPrivileges(indexName); @@ -303,9 +290,7 @@ export function registerReindexIndicesRoutes( { core: { savedObjects, - elasticsearch: { - legacy: { client: esClient }, - }, + elasticsearch: { client: esClient }, }, }, request, @@ -313,7 +298,7 @@ export function registerReindexIndicesRoutes( ) => { const { indexName } = request.params; const { client } = savedObjects; - const callAsCurrentUser = esClient.callAsCurrentUser.bind(esClient); + const callAsCurrentUser = esClient.asCurrentUser; const reindexActions = reindexActionsFactory(client, callAsCurrentUser); const reindexService = reindexServiceFactory( callAsCurrentUser, diff --git a/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts b/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts index daeb71ef12382..bdfbff61bc5db 100644 --- a/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts +++ b/x-pack/test/api_integration/apis/upgrade_assistant/upgrade_assistant.ts @@ -10,7 +10,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { reindexOperationWithLargeErrorMessage } from './reindex_operation_with_large_error_message'; export default function ({ getService }: FtrProviderContext) { - const es = getService('legacyEs'); + const es = getService('es'); describe('Reindex operation saved object', function () { const dotKibanaIndex = '.kibana'; diff --git a/x-pack/test/upgrade_assistant_integration/config.js b/x-pack/test/upgrade_assistant_integration/config.js index d11b39ff74e35..5409d8a470502 100644 --- a/x-pack/test/upgrade_assistant_integration/config.js +++ b/x-pack/test/upgrade_assistant_integration/config.js @@ -5,7 +5,6 @@ */ import path from 'path'; -import { LegacyEsProvider } from './services'; export default async function ({ readConfigFile }) { // Read the Kibana API integration tests config file so that we can utilize its services. @@ -25,7 +24,6 @@ export default async function ({ readConfigFile }) { services: { ...kibanaCommonConfig.get('services'), supertest: kibanaAPITestsConfig.get('services.supertest'), - legacyEs: LegacyEsProvider, }, esArchiver: xPackFunctionalTestsConfig.get('esArchiver'), junit: { diff --git a/x-pack/test/upgrade_assistant_integration/services/index.js b/x-pack/test/upgrade_assistant_integration/services/index.js deleted file mode 100644 index 83424ad1eb50c..0000000000000 --- a/x-pack/test/upgrade_assistant_integration/services/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { LegacyEsProvider } from './legacy_es'; diff --git a/x-pack/test/upgrade_assistant_integration/services/legacy_es.js b/x-pack/test/upgrade_assistant_integration/services/legacy_es.js deleted file mode 100644 index 78bfd63ded3c9..0000000000000 --- a/x-pack/test/upgrade_assistant_integration/services/legacy_es.js +++ /dev/null @@ -1,18 +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 { format as formatUrl } from 'url'; - -import * as legacyElasticsearch from 'elasticsearch'; - -export function LegacyEsProvider({ getService }) { - const config = getService('config'); - - return new legacyElasticsearch.Client({ - host: formatUrl(config.get('servers.elasticsearch')), - requestTimeout: config.get('timeouts.esRequestTimeout'), - }); -} diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js index 10074f68bb59e..f57d2184d4b8d 100644 --- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js +++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/reindexing.js @@ -13,7 +13,7 @@ import { getIndexState } from '../../../plugins/upgrade_assistant/common/get_ind export default function ({ getService }) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - const es = getService('legacyEs'); + const es = getService('es'); // Utility function that keeps polling API until reindex operation has completed or failed. const waitForReindexToComplete = async (indexName) => { @@ -65,14 +65,14 @@ export default function ({ getService }) { expect(lastState.status).to.equal(ReindexStatus.completed); const { newIndexName } = lastState; - const indexSummary = await es.indices.get({ index: 'dummydata' }); + const { body: indexSummary } = await es.indices.get({ index: 'dummydata' }); // The new index was created expect(indexSummary[newIndexName]).to.be.an('object'); // The original index name is aliased to the new one expect(indexSummary[newIndexName].aliases.dummydata).to.be.an('object'); // The number of documents in the new index matches what we expect - expect((await es.count({ index: lastState.newIndexName })).count).to.be(3); + expect((await es.count({ index: lastState.newIndexName })).body.count).to.be(3); // Cleanup newly created index await es.indices.delete({ @@ -95,9 +95,9 @@ export default function ({ getService }) { ], }, }); - expect((await es.count({ index: 'myAlias' })).count).to.be(3); - expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); - expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); + expect((await es.count({ index: 'myAlias' })).body.count).to.be(3); + expect((await es.count({ index: 'wildcardAlias' })).body.count).to.be(3); + expect((await es.count({ index: 'myHttpsAlias' })).body.count).to.be(2); // Reindex await supertest @@ -107,10 +107,10 @@ export default function ({ getService }) { const lastState = await waitForReindexToComplete('dummydata'); // The regular aliases should still return 3 docs - expect((await es.count({ index: 'myAlias' })).count).to.be(3); - expect((await es.count({ index: 'wildcardAlias' })).count).to.be(3); + expect((await es.count({ index: 'myAlias' })).body.count).to.be(3); + expect((await es.count({ index: 'wildcardAlias' })).body.count).to.be(3); // The filtered alias should still return 2 docs - expect((await es.count({ index: 'myHttpsAlias' })).count).to.be(2); + expect((await es.count({ index: 'myHttpsAlias' })).body.count).to.be(2); // Cleanup newly created index await es.indices.delete({ @@ -204,8 +204,8 @@ export default function ({ getService }) { await assertQueueState(undefined, 0); // Check that the closed index is still closed after reindexing - const resolvedIndices = await es.transport.request({ - path: `_resolve/index/${nameOfIndexThatShouldBeClosed}`, + const { body: resolvedIndices } = await es.indices.resolveIndex({ + name: nameOfIndexThatShouldBeClosed, }); const test1ReindexedState = getIndexState(nameOfIndexThatShouldBeClosed, resolvedIndices); From 46ac4ed7a2827b2dd689d30a68c54e29b726ee0c Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 27 Jan 2021 07:36:10 -0800 Subject: [PATCH 89/90] [CI] Combines Jest test jobs (#85850) Signed-off-by: Tyler Smalley --- .ci/es-snapshots/Jenkinsfile_verify_es | 1 - .ci/jobs.yml | 1 - .ci/teamcity/default/jest.sh | 10 ++------ .ci/teamcity/oss/jest.sh | 7 ++++-- .ci/teamcity/tests/jest.sh | 10 ++++++++ .teamcity/src/Extensions.kt | 15 ++++++------ .teamcity/src/builds/test/AllTests.kt | 2 +- .teamcity/src/builds/test/Jest.kt | 2 +- .teamcity/src/builds/test/XPackJest.kt | 19 --------------- .teamcity/src/projects/Kibana.kt | 1 - jest.config.integration.js | 5 +++- jest.config.js | 10 +++++++- jest.config.oss.js | 19 --------------- packages/kbn-test/jest-preset.js | 2 +- .../shell_scripts/extract_archives.sh | 2 +- test/scripts/jenkins_unit.sh | 17 +++----------- test/scripts/jenkins_xpack.sh | 23 ------------------- test/scripts/test/jest_integration.sh | 2 +- test/scripts/test/jest_unit.sh | 2 +- test/scripts/test/xpack_jest_unit.sh | 6 ----- vars/kibanaCoverage.groovy | 7 ------ vars/kibanaPipeline.groovy | 15 ++++++------ vars/tasks.groovy | 1 - x-pack/jest.config.js | 11 --------- 24 files changed, 55 insertions(+), 135 deletions(-) create mode 100755 .ci/teamcity/tests/jest.sh delete mode 100644 .teamcity/src/builds/test/XPackJest.kt delete mode 100644 jest.config.oss.js delete mode 100755 test/scripts/jenkins_xpack.sh delete mode 100755 test/scripts/test/xpack_jest_unit.sh delete mode 100644 x-pack/jest.config.js diff --git a/.ci/es-snapshots/Jenkinsfile_verify_es b/.ci/es-snapshots/Jenkinsfile_verify_es index 11a39faa9aed0..b40cd91a45c57 100644 --- a/.ci/es-snapshots/Jenkinsfile_verify_es +++ b/.ci/es-snapshots/Jenkinsfile_verify_es @@ -29,7 +29,6 @@ kibanaPipeline(timeoutMinutes: 150) { withEnv(["ES_SNAPSHOT_MANIFEST=${SNAPSHOT_MANIFEST}"]) { parallel([ 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), - 'x-pack-intake-agent': workers.intake('x-pack-intake', './test/scripts/jenkins_xpack.sh'), 'kibana-oss-agent': workers.functional('kibana-oss-tests', { kibanaPipeline.buildOss() }, [ 'oss-ciGroup1': kibanaPipeline.ossCiGroupProcess(1), 'oss-ciGroup2': kibanaPipeline.ossCiGroupProcess(2), diff --git a/.ci/jobs.yml b/.ci/jobs.yml index b05e834f5a459..6aa93d4a1056a 100644 --- a/.ci/jobs.yml +++ b/.ci/jobs.yml @@ -2,7 +2,6 @@ JOB: - kibana-intake - - x-pack-intake - kibana-firefoxSmoke - kibana-ciGroup1 - kibana-ciGroup2 diff --git a/.ci/teamcity/default/jest.sh b/.ci/teamcity/default/jest.sh index b900d1b6d6b4e..dac1cc8986a1c 100755 --- a/.ci/teamcity/default/jest.sh +++ b/.ci/teamcity/default/jest.sh @@ -1,10 +1,4 @@ #!/bin/bash -set -euo pipefail - -source "$(dirname "${0}")/../util.sh" - -export JOB=kibana-default-jest - -checks-reporter-with-killswitch "Jest Unit Tests" \ - node scripts/jest x-pack --ci --verbose --maxWorkers=5 +# This file is temporary and can be removed once #85850 has been +# merged and the changes included in open PR's (~3 days after merging) diff --git a/.ci/teamcity/oss/jest.sh b/.ci/teamcity/oss/jest.sh index 0dee07d00d2be..b323a88ef06bc 100755 --- a/.ci/teamcity/oss/jest.sh +++ b/.ci/teamcity/oss/jest.sh @@ -1,10 +1,13 @@ #!/bin/bash +# This file is temporary and can be removed once #85850 has been +# merged and the changes included in open PR's (~3 days after merging) + set -euo pipefail source "$(dirname "${0}")/../util.sh" export JOB=kibana-oss-jest -checks-reporter-with-killswitch "OSS Jest Unit Tests" \ - node scripts/jest --config jest.config.oss.js --ci --verbose --maxWorkers=5 +checks-reporter-with-killswitch "Jest Unit Tests" \ + node scripts/jest --ci --verbose diff --git a/.ci/teamcity/tests/jest.sh b/.ci/teamcity/tests/jest.sh new file mode 100755 index 0000000000000..c8b9b075e0e61 --- /dev/null +++ b/.ci/teamcity/tests/jest.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -euo pipefail + +source "$(dirname "${0}")/../util.sh" + +export JOB=kibana-jest + +checks-reporter-with-killswitch "Jest Unit Tests" \ + node scripts/jest --ci --verbose --coverage diff --git a/.teamcity/src/Extensions.kt b/.teamcity/src/Extensions.kt index 0a8abf4a149cf..ce99c9c49e198 100644 --- a/.teamcity/src/Extensions.kt +++ b/.teamcity/src/Extensions.kt @@ -20,20 +20,21 @@ fun BuildType.kibanaAgent(size: Int) { } val testArtifactRules = """ + target/junit/**/* target/kibana-* - target/test-metrics/* + target/kibana-coverage/**/* target/kibana-security-solution/**/*.png - target/junit/**/* + target/test-metrics/* target/test-suites-ci-plan.json - test/**/screenshots/session/*.png - test/**/screenshots/failure/*.png test/**/screenshots/diff/*.png + test/**/screenshots/failure/*.png + test/**/screenshots/session/*.png test/functional/failure_debug/html/*.html - x-pack/test/**/screenshots/session/*.png - x-pack/test/**/screenshots/failure/*.png x-pack/test/**/screenshots/diff/*.png - x-pack/test/functional/failure_debug/html/*.html + x-pack/test/**/screenshots/failure/*.png + x-pack/test/**/screenshots/session/*.png x-pack/test/functional/apps/reporting/reports/session/*.pdf + x-pack/test/functional/failure_debug/html/*.html """.trimIndent() fun BuildType.addTestSettings() { diff --git a/.teamcity/src/builds/test/AllTests.kt b/.teamcity/src/builds/test/AllTests.kt index 9506d98cbe50e..a49d5f2b07f4c 100644 --- a/.teamcity/src/builds/test/AllTests.kt +++ b/.teamcity/src/builds/test/AllTests.kt @@ -9,5 +9,5 @@ object AllTests : BuildType({ description = "All Non-Functional Tests" type = Type.COMPOSITE - dependsOn(QuickTests, Jest, XPackJest, JestIntegration, OssApiServerIntegration) + dependsOn(QuickTests, Jest, JestIntegration, OssApiServerIntegration) }) diff --git a/.teamcity/src/builds/test/Jest.kt b/.teamcity/src/builds/test/Jest.kt index c33c9c2678ca4..c9d170b5e5c3d 100644 --- a/.teamcity/src/builds/test/Jest.kt +++ b/.teamcity/src/builds/test/Jest.kt @@ -12,7 +12,7 @@ object Jest : BuildType({ kibanaAgent(8) steps { - runbld("Jest Unit", "./.ci/teamcity/oss/jest.sh") + runbld("Jest Unit", "./.ci/teamcity/tests/jest.sh") } addTestSettings() diff --git a/.teamcity/src/builds/test/XPackJest.kt b/.teamcity/src/builds/test/XPackJest.kt deleted file mode 100644 index 8246b60823ff9..0000000000000 --- a/.teamcity/src/builds/test/XPackJest.kt +++ /dev/null @@ -1,19 +0,0 @@ -package builds.test - -import addTestSettings -import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType -import kibanaAgent -import runbld - -object XPackJest : BuildType({ - name = "X-Pack Jest Unit" - description = "Executes X-Pack Jest Unit Tests" - - kibanaAgent(16) - - steps { - runbld("X-Pack Jest Unit", "./.ci/teamcity/default/jest.sh") - } - - addTestSettings() -}) diff --git a/.teamcity/src/projects/Kibana.kt b/.teamcity/src/projects/Kibana.kt index 5cddcf18e067f..c84b65027dee6 100644 --- a/.teamcity/src/projects/Kibana.kt +++ b/.teamcity/src/projects/Kibana.kt @@ -77,7 +77,6 @@ fun Kibana(config: KibanaConfiguration = KibanaConfiguration()) : Project { name = "Jest" buildType(Jest) - buildType(XPackJest) buildType(JestIntegration) } diff --git a/jest.config.integration.js b/jest.config.integration.js index 2064abb7e36a1..99728c5471dfb 100644 --- a/jest.config.integration.js +++ b/jest.config.integration.js @@ -17,6 +17,7 @@ module.exports = { testPathIgnorePatterns: preset.testPathIgnorePatterns.filter( (pattern) => !pattern.includes('integration_tests') ), + setupFilesAfterEnv: ['/packages/kbn-test/target/jest/setup/after_env.integration.js'], reporters: [ 'default', [ @@ -24,5 +25,7 @@ module.exports = { { reportName: 'Jest Integration Tests' }, ], ], - setupFilesAfterEnv: ['/packages/kbn-test/target/jest/setup/after_env.integration.js'], + coverageReporters: !!process.env.CI + ? [['json', { file: 'jest-integration.json' }]] + : ['html', 'text'], }; diff --git a/jest.config.js b/jest.config.js index f1833772c82a1..9ac5e57254e5a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,6 +7,14 @@ */ module.exports = { + preset: '@kbn/test', rootDir: '.', - projects: [...require('./jest.config.oss').projects, ...require('./x-pack/jest.config').projects], + projects: [ + '/packages/*/jest.config.js', + '/src/*/jest.config.js', + '/src/legacy/*/jest.config.js', + '/src/plugins/*/jest.config.js', + '/test/*/jest.config.js', + '/x-pack/plugins/*/jest.config.js', + ], }; diff --git a/jest.config.oss.js b/jest.config.oss.js deleted file mode 100644 index 1b478aa85bdba..0000000000000 --- a/jest.config.oss.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '.', - projects: [ - '/packages/*/jest.config.js', - '/src/*/jest.config.js', - '/src/legacy/*/jest.config.js', - '/src/plugins/*/jest.config.js', - '/test/*/jest.config.js', - ], -}; diff --git a/packages/kbn-test/jest-preset.js b/packages/kbn-test/jest-preset.js index ed88944ed862d..ebedb314f9594 100644 --- a/packages/kbn-test/jest-preset.js +++ b/packages/kbn-test/jest-preset.js @@ -19,7 +19,7 @@ module.exports = { coveragePathIgnorePatterns: ['/node_modules/', '.*\\.d\\.ts'], // A list of reporter names that Jest uses when writing coverage reports - coverageReporters: !!process.env.CODE_COVERAGE ? ['json'] : ['html', 'text'], + coverageReporters: !!process.env.CI ? [['json', { file: 'jest.json' }]] : ['html', 'text'], // An array of file extensions your modules use moduleFileExtensions: ['js', 'mjs', 'json', 'ts', 'tsx', 'node'], diff --git a/src/dev/code_coverage/shell_scripts/extract_archives.sh b/src/dev/code_coverage/shell_scripts/extract_archives.sh index 376467f9f2e55..14b35f8786d02 100644 --- a/src/dev/code_coverage/shell_scripts/extract_archives.sh +++ b/src/dev/code_coverage/shell_scripts/extract_archives.sh @@ -6,7 +6,7 @@ EXTRACT_DIR=/tmp/extracted_coverage mkdir -p $EXTRACT_DIR echo "### Extracting downloaded artifacts" -for x in kibana-intake x-pack-intake kibana-oss-tests kibana-xpack-tests; do +for x in kibana-intake kibana-oss-tests kibana-xpack-tests; do tar -xzf $DOWNLOAD_DIR/coverage/${x}/kibana-coverage.tar.gz -C $EXTRACT_DIR || echo "### Error 'tarring': ${x}" done diff --git a/test/scripts/jenkins_unit.sh b/test/scripts/jenkins_unit.sh index 6e28f9c3ef56a..9e387f97a016e 100755 --- a/test/scripts/jenkins_unit.sh +++ b/test/scripts/jenkins_unit.sh @@ -2,12 +2,6 @@ source test/scripts/jenkins_test_setup.sh -rename_coverage_file() { - test -f target/kibana-coverage/jest/coverage-final.json \ - && mv target/kibana-coverage/jest/coverage-final.json \ - target/kibana-coverage/jest/$1-coverage-final.json -} - if [[ -z "$CODE_COVERAGE" ]] ; then # Lint ./test/scripts/lint/eslint.sh @@ -34,13 +28,8 @@ if [[ -z "$CODE_COVERAGE" ]] ; then ./test/scripts/checks/test_hardening.sh else echo " -> Running jest tests with coverage" - node scripts/jest --ci --verbose --coverage --config jest.config.oss.js || true; - rename_coverage_file "oss" - echo "" - echo "" + node scripts/jest --ci --verbose --maxWorkers=6 --coverage || true; + echo " -> Running jest integration tests with coverage" - node --max-old-space-size=8192 scripts/jest_integration --ci --verbose --coverage || true; - rename_coverage_file "oss-integration" - echo "" - echo "" + node scripts/jest_integration --ci --verbose --coverage || true; fi diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh deleted file mode 100755 index 66fb5ae5370bc..0000000000000 --- a/test/scripts/jenkins_xpack.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -source test/scripts/jenkins_test_setup.sh - -if [[ -z "$CODE_COVERAGE" ]] ; then - echo " -> Running jest tests" - - ./test/scripts/test/xpack_jest_unit.sh -else - echo " -> Build runtime for canvas" - # build runtime for canvas - echo "NODE_ENV=$NODE_ENV" - node ./x-pack/plugins/canvas/scripts/shareable_runtime - echo " -> Running jest tests with coverage" - cd x-pack - node --max-old-space-size=6144 scripts/jest --ci --verbose --maxWorkers=5 --coverage --config jest.config.js || true; - # rename file in order to be unique one - test -f ../target/kibana-coverage/jest/coverage-final.json \ - && mv ../target/kibana-coverage/jest/coverage-final.json \ - ../target/kibana-coverage/jest/xpack-coverage-final.json - echo "" - echo "" -fi diff --git a/test/scripts/test/jest_integration.sh b/test/scripts/test/jest_integration.sh index 78ed804f88430..c48d9032466a3 100755 --- a/test/scripts/test/jest_integration.sh +++ b/test/scripts/test/jest_integration.sh @@ -3,4 +3,4 @@ source src/dev/ci_setup/setup_env.sh checks-reporter-with-killswitch "Jest Integration Tests" \ - node scripts/jest_integration --ci --verbose + node scripts/jest_integration --ci --verbose --coverage diff --git a/test/scripts/test/jest_unit.sh b/test/scripts/test/jest_unit.sh index 88c0fe528b88c..14d7268c6f36d 100755 --- a/test/scripts/test/jest_unit.sh +++ b/test/scripts/test/jest_unit.sh @@ -3,4 +3,4 @@ source src/dev/ci_setup/setup_env.sh checks-reporter-with-killswitch "Jest Unit Tests" \ - node scripts/jest --config jest.config.oss.js --ci --verbose --maxWorkers=5 + node scripts/jest --ci --verbose --maxWorkers=10 --coverage diff --git a/test/scripts/test/xpack_jest_unit.sh b/test/scripts/test/xpack_jest_unit.sh deleted file mode 100755 index 33b1c8a2b5183..0000000000000 --- a/test/scripts/test/xpack_jest_unit.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -checks-reporter-with-killswitch "X-Pack Jest" \ - node scripts/jest x-pack --ci --verbose --maxWorkers=5 diff --git a/vars/kibanaCoverage.groovy b/vars/kibanaCoverage.groovy index 609d8f78aeb96..e393f3a5d2150 100644 --- a/vars/kibanaCoverage.groovy +++ b/vars/kibanaCoverage.groovy @@ -197,13 +197,6 @@ def ingest(jobName, buildNumber, buildUrl, timestamp, previousSha, teamAssignmen def runTests() { parallel([ 'kibana-intake-agent': workers.intake('kibana-intake', './test/scripts/jenkins_unit.sh'), - 'x-pack-intake-agent': { - withEnv([ - 'NODE_ENV=test' // Needed for jest tests only - ]) { - workers.intake('x-pack-intake', './test/scripts/jenkins_xpack.sh')() - } - }, 'kibana-oss-agent' : workers.functional( 'kibana-oss-tests', { kibanaPipeline.buildOss() }, diff --git a/vars/kibanaPipeline.groovy b/vars/kibanaPipeline.groovy index 93cb7a719bbe8..e49692568cec8 100644 --- a/vars/kibanaPipeline.groovy +++ b/vars/kibanaPipeline.groovy @@ -179,20 +179,21 @@ def uploadCoverageArtifacts(prefix, pattern) { def withGcsArtifactUpload(workerName, closure) { def uploadPrefix = "kibana-ci-artifacts/jobs/${env.JOB_NAME}/${BUILD_NUMBER}/${workerName}" def ARTIFACT_PATTERNS = [ + 'target/junit/**/*', 'target/kibana-*', - 'target/test-metrics/*', + 'target/kibana-coverage/**/*', 'target/kibana-security-solution/**/*.png', - 'target/junit/**/*', + 'target/test-metrics/*', 'target/test-suites-ci-plan.json', - 'test/**/screenshots/session/*.png', - 'test/**/screenshots/failure/*.png', 'test/**/screenshots/diff/*.png', + 'test/**/screenshots/failure/*.png', + 'test/**/screenshots/session/*.png', 'test/functional/failure_debug/html/*.html', - 'x-pack/test/**/screenshots/session/*.png', - 'x-pack/test/**/screenshots/failure/*.png', 'x-pack/test/**/screenshots/diff/*.png', - 'x-pack/test/functional/failure_debug/html/*.html', + 'x-pack/test/**/screenshots/failure/*.png', + 'x-pack/test/**/screenshots/session/*.png', 'x-pack/test/functional/apps/reporting/reports/session/*.pdf', + 'x-pack/test/functional/failure_debug/html/*.html', ] withEnv([ diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 3493a95f0bdce..74ad1267e9355 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -35,7 +35,6 @@ def test() { kibanaPipeline.scriptTask('Jest Unit Tests', 'test/scripts/test/jest_unit.sh'), kibanaPipeline.scriptTask('API Integration Tests', 'test/scripts/test/api_integration.sh'), - kibanaPipeline.scriptTask('X-Pack Jest Unit Tests', 'test/scripts/test/xpack_jest_unit.sh'), ]) } diff --git a/x-pack/jest.config.js b/x-pack/jest.config.js deleted file mode 100644 index 8158987213cd2..0000000000000 --- a/x-pack/jest.config.js +++ /dev/null @@ -1,11 +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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '..', - projects: ['/x-pack/plugins/*/jest.config.js'], -}; From 82902e89189a0e74a94e206dc574033806675dae Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Wed, 27 Jan 2021 17:57:53 +0200 Subject: [PATCH 90/90] [Telemetry] Settings Collector: redact sensitive reported values (#88675) --- ...ana-plugin-core-public.uisettingsparams.md | 1 + ...-core-public.uisettingsparams.sensitive.md | 13 ++ ...re-server.iuisettingsclient.issensitive.md | 13 ++ ...na-plugin-core-server.iuisettingsclient.md | 1 + ...ana-plugin-core-server.uisettingsparams.md | 1 + ...-core-server.uisettingsparams.sensitive.md | 13 ++ .../src/tools/serializer.ts | 21 ++- src/core/public/public.api.md | 1 + src/core/server/server.api.md | 2 + .../ui_settings/settings/notifications.ts | 1 + src/core/server/ui_settings/types.ts | 4 + .../ui_settings/ui_settings_client.test.ts | 32 ++++ .../server/ui_settings/ui_settings_client.ts | 6 +- .../ui_settings/ui_settings_service.mock.ts | 1 + src/core/types/ui_settings.ts | 5 + src/plugins/kibana_usage_collection/README.md | 2 +- .../common/constants.ts | 4 + .../server/collectors/management/README.md | 51 +++++++ .../__snapshots__/index.test.ts.snap | 7 - .../collectors/management/index.test.ts | 61 -------- .../server/collectors/management/schema.ts | 51 +++++-- .../telemetry_management_collector.test.ts | 140 ++++++++++++++++++ .../telemetry_management_collector.ts | 19 +-- .../server/collectors/management/types.ts | 117 +++++++++++++++ src/plugins/telemetry/schema/oss_plugins.json | 83 ++++++++--- .../server/collector/collector.ts | 27 +++- .../vis_type_timelion/server/plugin.ts | 1 + .../dashboard_mode/server/ui_settings.ts | 1 + x-pack/plugins/reporting/server/plugin.ts | 1 + .../security_solution/server/ui_settings.ts | 4 + x-pack/test/usage_collection/config.ts | 5 +- .../stack_management_usage_test/kibana.json | 9 ++ .../public/index.ts | 11 ++ .../public/plugin.ts | 17 +++ .../public/types.ts | 14 ++ .../stack_management_usage_test/tsconfig.json | 12 ++ .../stack_management_usage/index.ts | 60 ++++++++ 37 files changed, 694 insertions(+), 118 deletions(-) create mode 100644 docs/development/core/public/kibana-plugin-core-public.uisettingsparams.sensitive.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.issensitive.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.uisettingsparams.sensitive.md create mode 100644 src/plugins/kibana_usage_collection/server/collectors/management/README.md delete mode 100644 src/plugins/kibana_usage_collection/server/collectors/management/__snapshots__/index.test.ts.snap delete mode 100644 src/plugins/kibana_usage_collection/server/collectors/management/index.test.ts create mode 100644 src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.test.ts create mode 100644 src/plugins/kibana_usage_collection/server/collectors/management/types.ts create mode 100644 x-pack/test/usage_collection/plugins/stack_management_usage_test/kibana.json create mode 100644 x-pack/test/usage_collection/plugins/stack_management_usage_test/public/index.ts create mode 100644 x-pack/test/usage_collection/plugins/stack_management_usage_test/public/plugin.ts create mode 100644 x-pack/test/usage_collection/plugins/stack_management_usage_test/public/types.ts create mode 100644 x-pack/test/usage_collection/plugins/stack_management_usage_test/tsconfig.json create mode 100644 x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md index 2cc149e2e2a79..0b7e6467667cb 100644 --- a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.md @@ -26,6 +26,7 @@ export interface UiSettingsParams | [readonly](./kibana-plugin-core-public.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-public.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | | [schema](./kibana-plugin-core-public.uisettingsparams.schema.md) | Type<T> | | +| [sensitive](./kibana-plugin-core-public.uisettingsparams.sensitive.md) | boolean | a flag indicating that value might contain user sensitive data. used by telemetry to mask the value of the setting when sent. | | [type](./kibana-plugin-core-public.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-public.uisettingstype.md) | | [validation](./kibana-plugin-core-public.uisettingsparams.validation.md) | ImageValidation | StringValidation | | | [value](./kibana-plugin-core-public.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.sensitive.md b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.sensitive.md new file mode 100644 index 0000000000000..e12f3c5649f17 --- /dev/null +++ b/docs/development/core/public/kibana-plugin-core-public.uisettingsparams.sensitive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [UiSettingsParams](./kibana-plugin-core-public.uisettingsparams.md) > [sensitive](./kibana-plugin-core-public.uisettingsparams.sensitive.md) + +## UiSettingsParams.sensitive property + +a flag indicating that value might contain user sensitive data. used by telemetry to mask the value of the setting when sent. + +Signature: + +```typescript +sensitive?: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.issensitive.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.issensitive.md new file mode 100644 index 0000000000000..a6f263e0b0f55 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.issensitive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [IUiSettingsClient](./kibana-plugin-core-server.iuisettingsclient.md) > [isSensitive](./kibana-plugin-core-server.iuisettingsclient.issensitive.md) + +## IUiSettingsClient.isSensitive property + +Shows whether the uiSetting is a sensitive value. Used by telemetry to not send sensitive values. + +Signature: + +```typescript +isSensitive: (key: string) => boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md index af99b5e5bb215..dd4a69c13a2d9 100644 --- a/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md +++ b/docs/development/core/server/kibana-plugin-core-server.iuisettingsclient.md @@ -21,6 +21,7 @@ export interface IUiSettingsClient | [getRegistered](./kibana-plugin-core-server.iuisettingsclient.getregistered.md) | () => Readonly<Record<string, PublicUiSettingsParams>> | Returns registered uiSettings values [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | | [getUserProvided](./kibana-plugin-core-server.iuisettingsclient.getuserprovided.md) | <T = any>() => Promise<Record<string, UserProvidedValues<T>>> | Retrieves a set of all uiSettings values set by the user. | | [isOverridden](./kibana-plugin-core-server.iuisettingsclient.isoverridden.md) | (key: string) => boolean | Shows whether the uiSettings value set by the user. | +| [isSensitive](./kibana-plugin-core-server.iuisettingsclient.issensitive.md) | (key: string) => boolean | Shows whether the uiSetting is a sensitive value. Used by telemetry to not send sensitive values. | | [remove](./kibana-plugin-core-server.iuisettingsclient.remove.md) | (key: string) => Promise<void> | Removes uiSettings value by key. | | [removeMany](./kibana-plugin-core-server.iuisettingsclient.removemany.md) | (keys: string[]) => Promise<void> | Removes multiple uiSettings values by keys. | | [set](./kibana-plugin-core-server.iuisettingsclient.set.md) | (key: string, value: any) => Promise<void> | Writes uiSettings value and marks it as set by the user. | diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md index 4dfde5200e7e9..d35afc4a149d1 100644 --- a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.md @@ -26,6 +26,7 @@ export interface UiSettingsParams | [readonly](./kibana-plugin-core-server.uisettingsparams.readonly.md) | boolean | a flag indicating that value cannot be changed | | [requiresPageReload](./kibana-plugin-core-server.uisettingsparams.requirespagereload.md) | boolean | a flag indicating whether new value applying requires page reloading | | [schema](./kibana-plugin-core-server.uisettingsparams.schema.md) | Type<T> | | +| [sensitive](./kibana-plugin-core-server.uisettingsparams.sensitive.md) | boolean | a flag indicating that value might contain user sensitive data. used by telemetry to mask the value of the setting when sent. | | [type](./kibana-plugin-core-server.uisettingsparams.type.md) | UiSettingsType | defines a type of UI element [UiSettingsType](./kibana-plugin-core-server.uisettingstype.md) | | [validation](./kibana-plugin-core-server.uisettingsparams.validation.md) | ImageValidation | StringValidation | | | [value](./kibana-plugin-core-server.uisettingsparams.value.md) | T | default value to fall back to if a user doesn't provide any | diff --git a/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.sensitive.md b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.sensitive.md new file mode 100644 index 0000000000000..f2c7de19dde1a --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.uisettingsparams.sensitive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) > [sensitive](./kibana-plugin-core-server.uisettingsparams.sensitive.md) + +## UiSettingsParams.sensitive property + +a flag indicating that value might contain user sensitive data. used by telemetry to mask the value of the setting when sent. + +Signature: + +```typescript +sensitive?: boolean; +``` diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index a941fa2a9d01f..bad40b5388407 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -19,7 +19,7 @@ export enum TelemetryKinds { Date = 10001, } -interface DescriptorValue { +export interface DescriptorValue { kind: ts.SyntaxKind | TelemetryKinds; type: keyof typeof ts.SyntaxKind | keyof typeof TelemetryKinds; } @@ -42,6 +42,13 @@ export function isObjectDescriptor(value: any) { return false; } +export function descriptorToObject(descriptor: Descriptor | DescriptorValue) { + return Object.entries(descriptor).reduce((acc, [key, value]) => { + acc[key] = value.kind ? kindToDescriptorName(value.kind) : descriptorToObject(value); + return acc; + }, {} as Record); +} + export function kindToDescriptorName(kind: number) { switch (kind) { case ts.SyntaxKind.StringKeyword: @@ -158,6 +165,16 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | if (symbolName === 'Date') { return { kind: TelemetryKinds.Date, type: 'Date' }; } + + // Support Array + if (symbolName === 'Array') { + if (node.typeArguments?.length !== 1) { + throw Error('Array type only supports 1 type parameter Array'); + } + const typeArgument = node.typeArguments[0]; + return { items: getDescriptor(typeArgument, program) }; + } + // Support `Record` if (symbolName === 'Record') { const descriptor = getDescriptor(node.typeArguments![1], program); @@ -243,7 +260,7 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | case ts.SyntaxKind.UnionType: case ts.SyntaxKind.AnyKeyword: default: - throw new Error(`Unknown type ${ts.SyntaxKind[node.kind]}; ${node.getText()}`); + throw new Error(`Unknown type ${ts.SyntaxKind[node.kind]}`); } } diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index d72b2aa5afd1e..0097127924a5c 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -1507,6 +1507,7 @@ export interface UiSettingsParams { requiresPageReload?: boolean; // (undocumented) schema: Type; + sensitive?: boolean; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 0b058011267eb..fc90284ffe5b2 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1195,6 +1195,7 @@ export interface IUiSettingsClient { getRegistered: () => Readonly>; getUserProvided: () => Promise>>; isOverridden: (key: string) => boolean; + isSensitive: (key: string) => boolean; remove: (key: string) => Promise; removeMany: (keys: string[]) => Promise; set: (key: string, value: any) => Promise; @@ -3100,6 +3101,7 @@ export interface UiSettingsParams { requiresPageReload?: boolean; // (undocumented) schema: Type; + sensitive?: boolean; type?: UiSettingsType; // (undocumented) validation?: ImageValidation | StringValidation; diff --git a/src/core/server/ui_settings/settings/notifications.ts b/src/core/server/ui_settings/settings/notifications.ts index a60732ecb807a..cedd52289a68c 100644 --- a/src/core/server/ui_settings/settings/notifications.ts +++ b/src/core/server/ui_settings/settings/notifications.ts @@ -35,6 +35,7 @@ export const getNotificationsSettings = (): Record => }, }), category: ['notifications'], + sensitive: true, schema: schema.string(), }, 'notifications:lifetime:banner': { diff --git a/src/core/server/ui_settings/types.ts b/src/core/server/ui_settings/types.ts index eb2d8b00dc488..73f46e4db3b2c 100644 --- a/src/core/server/ui_settings/types.ts +++ b/src/core/server/ui_settings/types.ts @@ -65,6 +65,10 @@ export interface IUiSettingsClient { * Shows whether the uiSettings value set by the user. */ isOverridden: (key: string) => boolean; + /** + * Shows whether the uiSetting is a sensitive value. Used by telemetry to not send sensitive values. + */ + isSensitive: (key: string) => boolean; } /** @internal */ diff --git a/src/core/server/ui_settings/ui_settings_client.test.ts b/src/core/server/ui_settings/ui_settings_client.test.ts index 7fa5a85e5154e..26c4d9e8e6dd9 100644 --- a/src/core/server/ui_settings/ui_settings_client.test.ts +++ b/src/core/server/ui_settings/ui_settings_client.test.ts @@ -644,6 +644,38 @@ describe('ui settings', () => { }); }); + describe('#isSensitive()', () => { + it('returns false if sensitive config is not set', () => { + const defaults = { + foo: { + schema: schema.string(), + value: '1', + }, + }; + + const { uiSettings } = setup({ defaults }); + expect(uiSettings.isSensitive('foo')).toBe(false); + }); + + it('returns false if key is not in the settings', () => { + const { uiSettings } = setup(); + expect(uiSettings.isSensitive('baz')).toBe(false); + }); + + it('returns true if overrides defined and key is overridden', () => { + const defaults = { + foo: { + schema: schema.string(), + sensitive: true, + value: '1', + }, + }; + + const { uiSettings } = setup({ defaults }); + expect(uiSettings.isSensitive('foo')).toBe(true); + }); + }); + describe('#isOverridden()', () => { it('returns false if no overrides defined', () => { const { uiSettings } = setup(); diff --git a/src/core/server/ui_settings/ui_settings_client.ts b/src/core/server/ui_settings/ui_settings_client.ts index 2ba8caaba5170..b8a46a2f994aa 100644 --- a/src/core/server/ui_settings/ui_settings_client.ts +++ b/src/core/server/ui_settings/ui_settings_client.ts @@ -52,7 +52,6 @@ export class UiSettingsClient implements IUiSettingsClient { constructor(options: UiSettingsServiceOptions) { const { type, id, buildNum, savedObjectsClient, log, defaults = {}, overrides = {} } = options; - this.type = type; this.id = id; this.buildNum = buildNum; @@ -132,6 +131,11 @@ export class UiSettingsClient implements IUiSettingsClient { return this.overrides.hasOwnProperty(key); } + isSensitive(key: string): boolean { + const definition = this.defaults[key]; + return !!definition?.sensitive; + } + private assertUpdateAllowed(key: string) { if (this.isOverridden(key)) { throw new CannotOverrideError(`Unable to update "${key}" because it is overridden`); diff --git a/src/core/server/ui_settings/ui_settings_service.mock.ts b/src/core/server/ui_settings/ui_settings_service.mock.ts index a03412e37f551..771b9d243656a 100644 --- a/src/core/server/ui_settings/ui_settings_service.mock.ts +++ b/src/core/server/ui_settings/ui_settings_service.mock.ts @@ -25,6 +25,7 @@ const createClientMock = () => { remove: jest.fn(), removeMany: jest.fn(), isOverridden: jest.fn(), + isSensitive: jest.fn(), }; mocked.get.mockResolvedValue(false); mocked.getAll.mockResolvedValue({}); diff --git a/src/core/types/ui_settings.ts b/src/core/types/ui_settings.ts index 24dfbbeea6726..92e8f6ef2f41e 100644 --- a/src/core/types/ui_settings.ts +++ b/src/core/types/ui_settings.ts @@ -56,6 +56,11 @@ export interface UiSettingsParams { requiresPageReload?: boolean; /** a flag indicating that value cannot be changed */ readonly?: boolean; + /** + * a flag indicating that value might contain user sensitive data. + * used by telemetry to mask the value of the setting when sent. + */ + sensitive?: boolean; /** defines a type of UI element {@link UiSettingsType} */ type?: UiSettingsType; /** optional deprecation information. Used to generate a deprecation warning. */ diff --git a/src/plugins/kibana_usage_collection/README.md b/src/plugins/kibana_usage_collection/README.md index 69711d30cdc74..85d362cf0a9b1 100644 --- a/src/plugins/kibana_usage_collection/README.md +++ b/src/plugins/kibana_usage_collection/README.md @@ -6,6 +6,6 @@ This plugin registers the basic usage collectors from Kibana: - UI Metrics - Ops stats - Number of Saved Objects per type -- Non-default UI Settings +- [User-changed UI Settings](./server/collectors/management/README.md) - CSP configuration - Core Metrics diff --git a/src/plugins/kibana_usage_collection/common/constants.ts b/src/plugins/kibana_usage_collection/common/constants.ts index 4505c59e0f630..052367765a6ec 100644 --- a/src/plugins/kibana_usage_collection/common/constants.ts +++ b/src/plugins/kibana_usage_collection/common/constants.ts @@ -13,3 +13,7 @@ export const PLUGIN_NAME = 'kibana_usage_collection'; * The type name used to publish Kibana usage stats in the formatted as bulk. */ export const KIBANA_STATS_TYPE = 'kibana_stats'; +/** + * Redacted keyword; used as a value for sensitive ui settings + */ +export const REDACTED_KEYWORD = '[REDACTED]'; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/README.md b/src/plugins/kibana_usage_collection/server/collectors/management/README.md new file mode 100644 index 0000000000000..b539136d57b89 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/management/README.md @@ -0,0 +1,51 @@ +# User-changed UI Settings - Management Collector + +The Usage Collector `stack_management` reports user changed settings. +All user changed UI Settings are automatically collected. + +After adding a new setting you will be required to do the following steps: + +1. Update the [schema](./schema.ts) to include the setting name and schema type. +``` +export const stackManagementSchema: MakeSchemaFrom = { + 'MY_UI_SETTING': { type: 'keyword' }, +} +``` + +2. Update the [UsageStats interface](./types.ts) with the setting name and typescript type. +``` +export interface UsageStats { + 'MY_UI_SETTING': string; +} +``` +3. Run the telemetry checker with `--fix` flag to automatically fix the mappings + +``` +node scripts/telemetry_check --fix +``` + +If you forget any of the steps our telemetry tools and tests will help you through the process! + +## Sensitive fields + +If the configured UI setting might contain user sensitive information simply add the property `sensitive: true` to the ui setting registration config. + +``` +uiSettings.register({ + [NEWS_FEED_URL_SETTING]: { + name: i18n.translate('xpack.securitySolution.uiSettings.newsFeedUrl', { + defaultMessage: 'News feed URL', + }), + value: NEWS_FEED_URL_SETTING_DEFAULT, + sensitive: true, + description: i18n.translate('xpack.securitySolution.uiSettings.newsFeedUrlDescription', { + defaultMessage: '

News feed content will be retrieved from this URL

', + }), + category: [APP_ID], + requiresPageReload: true, + schema: schema.string(), + }, +}), +``` + +The value of any UI setting marked as `sensitive` will be reported as a keyword `[REDACTED]` instead of the actual value. This hides the actual sensitive information while giving us some intelligence over which fields the users are interactive with the most. diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/__snapshots__/index.test.ts.snap b/src/plugins/kibana_usage_collection/server/collectors/management/__snapshots__/index.test.ts.snap deleted file mode 100644 index def230dea8d70..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/management/__snapshots__/index.test.ts.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`telemetry_application_usage_collector fetch() 1`] = ` -Object { - "my-key": "my-value", -} -`; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/index.test.ts b/src/plugins/kibana_usage_collection/server/collectors/management/index.test.ts deleted file mode 100644 index 38baf02d6fe1b..0000000000000 --- a/src/plugins/kibana_usage_collection/server/collectors/management/index.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * and the Server Side Public License, v 1; you may not use this file except in - * compliance with, at your election, the Elastic License or the Server Side - * Public License, v 1. - */ - -import { loggingSystemMock, uiSettingsServiceMock } from '../../../../../core/server/mocks'; -import { - Collector, - createUsageCollectionSetupMock, - createCollectorFetchContextMock, -} from '../../../../usage_collection/server/usage_collection.mock'; - -import { registerManagementUsageCollector } from './'; - -const logger = loggingSystemMock.createLogger(); - -describe('telemetry_application_usage_collector', () => { - let collector: Collector; - - const usageCollectionMock = createUsageCollectionSetupMock(); - usageCollectionMock.makeUsageCollector.mockImplementation((config) => { - collector = new Collector(logger, config); - return createUsageCollectionSetupMock().makeUsageCollector(config); - }); - - const uiSettingsClient = uiSettingsServiceMock.createClient(); - const getUiSettingsClient = jest.fn(() => uiSettingsClient); - const mockedFetchContext = createCollectorFetchContextMock(); - - beforeAll(() => { - registerManagementUsageCollector(usageCollectionMock, getUiSettingsClient); - }); - - test('registered collector is set', () => { - expect(collector).not.toBeUndefined(); - }); - - test('isReady() => false if no client', () => { - getUiSettingsClient.mockImplementationOnce(() => undefined as any); - expect(collector.isReady()).toBe(false); - }); - - test('isReady() => true', () => { - expect(collector.isReady()).toBe(true); - }); - - test('fetch()', async () => { - uiSettingsClient.getUserProvided.mockImplementationOnce(async () => ({ - 'my-key': { userValue: 'my-value' }, - })); - await expect(collector.fetch(mockedFetchContext)).resolves.toMatchSnapshot(); - }); - - test('fetch() should not fail if invoked when not ready', async () => { - getUiSettingsClient.mockImplementationOnce(() => undefined as any); - await expect(collector.fetch(mockedFetchContext)).resolves.toBe(undefined); - }); -}); diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 28eeb461f7a86..b644f282c1f36 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -7,18 +7,25 @@ */ import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; -import { UsageStats } from './telemetry_management_collector'; +import { UsageStats } from './types'; -// Retrieved by changing all the current settings in Kibana (we'll need to revisit it in the future). -// I would suggest we use flattened type for the mappings of this collector. export const stackManagementSchema: MakeSchemaFrom = { + // sensitive + 'timelion:quandl.key': { type: 'keyword' }, + 'securitySolution:defaultIndex': { type: 'keyword' }, + 'securitySolution:newsFeedUrl': { type: 'keyword' }, + 'xpackReporting:customPdfLogo': { type: 'keyword' }, + 'notifications:banner': { type: 'keyword' }, + 'timelion:graphite.url': { type: 'keyword' }, + 'xpackDashboardMode:roles': { type: 'keyword' }, + 'securitySolution:ipReputationLinks': { type: 'keyword' }, + // non-sensitive 'visualize:enableLabs': { type: 'boolean' }, 'visualization:heatmap:maxBuckets': { type: 'long' }, 'visualization:colorMapping': { type: 'text' }, 'visualization:regionmap:showWarnings': { type: 'boolean' }, 'visualization:dimmingOpacity': { type: 'float' }, 'visualization:tileMap:maxPrecision': { type: 'long' }, - 'securitySolution:ipReputationLinks': { type: 'text' }, 'csv:separator': { type: 'keyword' }, 'visualization:tileMap:WMSdefaults': { type: 'text' }, 'timelion:target_buckets': { type: 'long' }, @@ -27,14 +34,11 @@ export const stackManagementSchema: MakeSchemaFrom = { 'timelion:min_interval': { type: 'keyword' }, 'timelion:default_rows': { type: 'long' }, 'timelion:default_columns': { type: 'long' }, - 'timelion:quandl.key': { type: 'keyword' }, 'timelion:es.default_index': { type: 'keyword' }, 'timelion:showTutorial': { type: 'boolean' }, 'securitySolution:timeDefaults': { type: 'keyword' }, 'securitySolution:defaultAnomalyScore': { type: 'long' }, - 'securitySolution:defaultIndex': { type: 'keyword' }, // it's an array 'securitySolution:refreshIntervalDefaults': { type: 'keyword' }, - 'securitySolution:newsFeedUrl': { type: 'keyword' }, 'securitySolution:enableNewsFeed': { type: 'boolean' }, 'search:includeFrozen': { type: 'boolean' }, 'courier:maxConcurrentShardRequests': { type: 'long' }, @@ -43,21 +47,29 @@ export const stackManagementSchema: MakeSchemaFrom = { 'courier:customRequestPreference': { type: 'keyword' }, 'courier:ignoreFilterIfFieldNotInIndex': { type: 'boolean' }, 'rollups:enableIndexPatterns': { type: 'boolean' }, - 'xpackReporting:customPdfLogo': { type: 'text' }, 'notifications:lifetime:warning': { type: 'long' }, 'notifications:lifetime:banner': { type: 'long' }, 'notifications:lifetime:info': { type: 'long' }, - 'notifications:banner': { type: 'text' }, 'notifications:lifetime:error': { type: 'long' }, 'doc_table:highlight': { type: 'boolean' }, 'discover:searchOnPageLoad': { type: 'boolean' }, // eslint-disable-next-line @typescript-eslint/naming-convention 'doc_table:hideTimeColumn': { type: 'boolean' }, 'discover:sampleSize': { type: 'long' }, - defaultColumns: { type: 'keyword' }, // it's an array + defaultColumns: { + type: 'array', + items: { + type: 'keyword', + }, + }, 'context:defaultSize': { type: 'long' }, 'discover:aggs:terms:size': { type: 'long' }, - 'context:tieBreakerFields': { type: 'keyword' }, // it's an array + 'context:tieBreakerFields': { + type: 'array', + items: { + type: 'keyword', + }, + }, 'discover:sort:defaultOrder': { type: 'keyword' }, 'context:step': { type: 'long' }, 'accessibility:disableAnimations': { type: 'boolean' }, @@ -79,7 +91,12 @@ export const stackManagementSchema: MakeSchemaFrom = { 'query:queryString:options': { type: 'keyword' }, 'metrics:max_buckets': { type: 'long' }, 'query:allowLeadingWildcards': { type: 'boolean' }, - metaFields: { type: 'keyword' }, // it's an array + metaFields: { + type: 'array', + items: { + type: 'keyword', + }, + }, 'indexPattern:placeholder': { type: 'keyword' }, 'histogram:barTarget': { type: 'long' }, 'histogram:maxBars': { type: 'long' }, @@ -101,4 +118,14 @@ export const stackManagementSchema: MakeSchemaFrom = { 'csv:quoteValues': { type: 'boolean' }, 'dateFormat:dow': { type: 'keyword' }, dateFormat: { type: 'keyword' }, + 'autocomplete:useTimeRange': { type: 'boolean' }, + 'search:timeout': { type: 'long' }, + 'visualization:visualize:legacyChartsLibrary': { type: 'boolean' }, + 'doc_table:legacy': { type: 'boolean' }, + 'discover:modifyColumnsOnSwitch': { type: 'boolean' }, + 'discover:searchFieldsFromSource': { type: 'boolean' }, + 'securitySolution:rulesTableRefresh': { type: 'text' }, + 'apm:enableSignificantTerms': { type: 'boolean' }, + 'apm:enableServiceOverview': { type: 'boolean' }, + 'apm:enableCorrelations': { type: 'boolean' }, }; diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.test.ts b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.test.ts new file mode 100644 index 0000000000000..4bcd98f894e2a --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.test.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +import { loggingSystemMock, uiSettingsServiceMock } from '../../../../../core/server/mocks'; +import { + Collector, + createUsageCollectionSetupMock, + createCollectorFetchContextMock, +} from '../../../../usage_collection/server/usage_collection.mock'; + +import { + registerManagementUsageCollector, + createCollectorFetch, +} from './telemetry_management_collector'; + +const logger = loggingSystemMock.createLogger(); + +describe('telemetry_application_usage_collector', () => { + let collector: Collector; + + const usageCollectionMock = createUsageCollectionSetupMock(); + usageCollectionMock.makeUsageCollector.mockImplementation((config) => { + collector = new Collector(logger, config); + return createUsageCollectionSetupMock().makeUsageCollector(config); + }); + + const uiSettingsClient = uiSettingsServiceMock.createClient(); + const getUiSettingsClient = jest.fn(() => uiSettingsClient); + const mockedFetchContext = createCollectorFetchContextMock(); + + beforeAll(() => { + registerManagementUsageCollector(usageCollectionMock, getUiSettingsClient); + }); + + test('registered collector is set', () => { + expect(collector).not.toBeUndefined(); + }); + + test('isReady() => false if no client', () => { + getUiSettingsClient.mockImplementationOnce(() => undefined as any); + expect(collector.isReady()).toBe(false); + }); + + test('isReady() => true', () => { + expect(collector.isReady()).toBe(true); + }); + + test('fetch()', async () => { + uiSettingsClient.getUserProvided.mockImplementationOnce(async () => ({ + 'visualization:colorMapping': { userValue: 'red' }, + })); + await expect(collector.fetch(mockedFetchContext)).resolves.toEqual({ + 'visualization:colorMapping': 'red', + }); + }); + + test('fetch() should not fail if invoked when not ready', async () => { + getUiSettingsClient.mockImplementationOnce(() => undefined as any); + await expect(collector.fetch(mockedFetchContext)).resolves.toBe(undefined); + }); +}); + +describe('createCollectorFetch', () => { + const mockUserSettings = { + item1: { userValue: 'test' }, + item2: { userValue: 123 }, + item3: { userValue: false }, + }; + + const mockIsSensitive = (key: string) => { + switch (key) { + case 'item1': + case 'item2': + return false; + case 'item3': + return true; + default: + throw new Error(`Unexpected ui setting: ${key}`); + } + }; + + it('returns #fetchUsageStats function', () => { + const getUiSettingsClient = jest.fn(() => undefined); + const fetchFunction = createCollectorFetch(getUiSettingsClient); + expect(typeof fetchFunction).toBe('function'); + }); + + describe('#fetchUsageStats', () => { + it('returns undefined if no uiSettingsClient returned from getUiSettingsClient', async () => { + const getUiSettingsClient = jest.fn(() => undefined); + const fetchFunction = createCollectorFetch(getUiSettingsClient); + const result = await fetchFunction(); + expect(result).toBe(undefined); + expect(getUiSettingsClient).toBeCalledTimes(1); + }); + + it('returns all user changed settings', async () => { + const uiSettingsClient = uiSettingsServiceMock.createClient(); + const getUiSettingsClient = jest.fn(() => uiSettingsClient); + uiSettingsClient.getUserProvided.mockResolvedValue(mockUserSettings); + uiSettingsClient.isSensitive.mockImplementation(mockIsSensitive); + const fetchFunction = createCollectorFetch(getUiSettingsClient); + const result = await fetchFunction(); + expect(typeof result).toBe('object'); + expect(Object.keys(result!)).toEqual(Object.keys(mockUserSettings)); + }); + + it('returns the actual values of non-sensitive settings', async () => { + const uiSettingsClient = uiSettingsServiceMock.createClient(); + const getUiSettingsClient = jest.fn(() => uiSettingsClient); + uiSettingsClient.getUserProvided.mockResolvedValue(mockUserSettings); + uiSettingsClient.isSensitive.mockImplementation(mockIsSensitive); + const fetchFunction = createCollectorFetch(getUiSettingsClient); + const result = await fetchFunction(); + expect(typeof result).toBe('object'); + expect(result!).toMatchObject({ + item1: 'test', + item2: 123, + }); + }); + + it('returns [REDACTED] as a value for sensitive settings', async () => { + const uiSettingsClient = uiSettingsServiceMock.createClient(); + const getUiSettingsClient = jest.fn(() => uiSettingsClient); + uiSettingsClient.getUserProvided.mockResolvedValue(mockUserSettings); + uiSettingsClient.isSensitive.mockImplementation(mockIsSensitive); + const fetchFunction = createCollectorFetch(getUiSettingsClient); + const result = await fetchFunction(); + expect(typeof result).toBe('object'); + expect(result!).toMatchObject({ + item3: '[REDACTED]', + }); + }); + }); +}); diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts index c45f3d6139d95..651fbbd5a897a 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/telemetry_management_collector.ts @@ -9,12 +9,8 @@ import { IUiSettingsClient } from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { stackManagementSchema } from './schema'; - -export interface UsageStats extends Record { - // We don't support `type` yet. Only interfaces. So I added at least 1 known key to the generic - // Record extension to avoid eslint reverting it back to a `type` - 'visualize:enableLabs': boolean; -} +import { UsageStats } from './types'; +import { REDACTED_KEYWORD } from '../../../common/constants'; export function createCollectorFetch(getUiSettingsClient: () => IUiSettingsClient | undefined) { return async function fetchUsageStats(): Promise { @@ -23,11 +19,12 @@ export function createCollectorFetch(getUiSettingsClient: () => IUiSettingsClien return; } - const user = await uiSettingsClient.getUserProvided(); - const modifiedEntries = Object.keys(user) - .filter((key: string) => key !== 'buildNum') - .reduce((obj: any, key: string) => { - obj[key] = user[key].userValue; + const userProvided = await uiSettingsClient.getUserProvided(); + const modifiedEntries = Object.entries(userProvided) + .filter(([key]) => key !== 'buildNum') + .reduce((obj: any, [key, { userValue }]) => { + const sensitive = uiSettingsClient.isSensitive(key); + obj[key] = sensitive ? REDACTED_KEYWORD : userValue; return obj; }, {}); diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts new file mode 100644 index 0000000000000..417841ee89569 --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * and the Server Side Public License, v 1; you may not use this file except in + * compliance with, at your election, the Elastic License or the Server Side + * Public License, v 1. + */ + +export interface UsageStats { + /** + * sensitive settings + */ + 'timelion:quandl.key': string; + 'securitySolution:defaultIndex': string; + 'securitySolution:newsFeedUrl': string; + 'xpackReporting:customPdfLogo': string; + 'notifications:banner': string; + 'timelion:graphite.url': string; + 'xpackDashboardMode:roles': string; + 'securitySolution:ipReputationLinks': string; + /** + * non-sensitive settings + */ + 'autocomplete:useTimeRange': boolean; + 'search:timeout': number; + 'visualization:visualize:legacyChartsLibrary': boolean; + 'doc_table:legacy': boolean; + 'discover:modifyColumnsOnSwitch': boolean; + 'discover:searchFieldsFromSource': boolean; + 'securitySolution:rulesTableRefresh': string; + 'apm:enableSignificantTerms': boolean; + 'apm:enableServiceOverview': boolean; + 'apm:enableCorrelations': boolean; + 'visualize:enableLabs': boolean; + 'visualization:heatmap:maxBuckets': number; + 'visualization:colorMapping': string; + 'visualization:regionmap:showWarnings': boolean; + 'visualization:dimmingOpacity': number; + 'visualization:tileMap:maxPrecision': number; + 'csv:separator': string; + 'visualization:tileMap:WMSdefaults': string; + 'timelion:target_buckets': number; + 'timelion:max_buckets': number; + 'timelion:es.timefield': string; + 'timelion:min_interval': string; + 'timelion:default_rows': number; + 'timelion:default_columns': number; + 'timelion:es.default_index': string; + 'timelion:showTutorial': boolean; + 'securitySolution:timeDefaults': string; + 'securitySolution:defaultAnomalyScore': number; + 'securitySolution:refreshIntervalDefaults': string; + 'securitySolution:enableNewsFeed': boolean; + 'search:includeFrozen': boolean; + 'courier:maxConcurrentShardRequests': number; + 'courier:batchSearches': boolean; + 'courier:setRequestPreference': string; + 'courier:customRequestPreference': string; + 'courier:ignoreFilterIfFieldNotInIndex': boolean; + 'rollups:enableIndexPatterns': boolean; + 'notifications:lifetime:warning': number; + 'notifications:lifetime:banner': number; + 'notifications:lifetime:info': number; + 'notifications:lifetime:error': number; + 'doc_table:highlight': boolean; + 'discover:searchOnPageLoad': boolean; + // eslint-disable-next-line @typescript-eslint/naming-convention + 'doc_table:hideTimeColumn': boolean; + 'discover:sampleSize': number; + defaultColumns: string[]; + 'context:defaultSize': number; + 'discover:aggs:terms:size': number; + 'context:tieBreakerFields': string[]; + 'discover:sort:defaultOrder': string; + 'context:step': number; + 'accessibility:disableAnimations': boolean; + 'ml:fileDataVisualizerMaxFileSize': string; + 'ml:anomalyDetection:results:enableTimeDefaults': boolean; + 'ml:anomalyDetection:results:timeDefaults': string; + 'truncate:maxHeight': number; + 'timepicker:timeDefaults': string; + 'timepicker:refreshIntervalDefaults': string; + 'timepicker:quickRanges': string; + 'theme:version': string; + 'theme:darkMode': boolean; + 'state:storeInSessionStorage': boolean; + 'savedObjects:perPage': number; + 'search:queryLanguage': string; + 'shortDots:enable': boolean; + 'sort:options': string; + 'savedObjects:listingLimit': number; + 'query:queryString:options': string; + 'metrics:max_buckets': number; + 'query:allowLeadingWildcards': boolean; + metaFields: string[]; + 'indexPattern:placeholder': string; + 'histogram:barTarget': number; + 'histogram:maxBars': number; + 'format:number:defaultLocale': string; + 'format:percent:defaultPattern': string; + 'format:number:defaultPattern': string; + 'history:limit': number; + 'format:defaultTypeMap': string; + 'format:currency:defaultPattern': string; + defaultIndex: string; + 'format:bytes:defaultPattern': string; + 'filters:pinnedByDefault': boolean; + 'filterEditor:suggestValues': boolean; + 'fields:popularLimit': number; + dateNanosFormat: string; + defaultRoute: string; + 'dateFormat:tz': string; + 'dateFormat:scaled': string; + 'csv:quoteValues': boolean; + 'dateFormat:dow': string; + dateFormat: string; +} diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 7bac6a809eca3..950fdf9405b75 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -4076,6 +4076,27 @@ }, "stack_management": { "properties": { + "timelion:quandl.key": { + "type": "keyword" + }, + "securitySolution:defaultIndex": { + "type": "keyword" + }, + "securitySolution:newsFeedUrl": { + "type": "keyword" + }, + "xpackReporting:customPdfLogo": { + "type": "keyword" + }, + "notifications:banner": { + "type": "keyword" + }, + "timelion:graphite.url": { + "type": "keyword" + }, + "xpackDashboardMode:roles": { + "type": "keyword" + }, "visualize:enableLabs": { "type": "boolean" }, @@ -4095,7 +4116,7 @@ "type": "long" }, "securitySolution:ipReputationLinks": { - "type": "text" + "type": "keyword" }, "csv:separator": { "type": "keyword" @@ -4121,9 +4142,6 @@ "timelion:default_columns": { "type": "long" }, - "timelion:quandl.key": { - "type": "keyword" - }, "timelion:es.default_index": { "type": "keyword" }, @@ -4136,15 +4154,9 @@ "securitySolution:defaultAnomalyScore": { "type": "long" }, - "securitySolution:defaultIndex": { - "type": "keyword" - }, "securitySolution:refreshIntervalDefaults": { "type": "keyword" }, - "securitySolution:newsFeedUrl": { - "type": "keyword" - }, "securitySolution:enableNewsFeed": { "type": "boolean" }, @@ -4169,9 +4181,6 @@ "rollups:enableIndexPatterns": { "type": "boolean" }, - "xpackReporting:customPdfLogo": { - "type": "text" - }, "notifications:lifetime:warning": { "type": "long" }, @@ -4181,9 +4190,6 @@ "notifications:lifetime:info": { "type": "long" }, - "notifications:banner": { - "type": "text" - }, "notifications:lifetime:error": { "type": "long" }, @@ -4200,7 +4206,10 @@ "type": "long" }, "defaultColumns": { - "type": "keyword" + "type": "array", + "items": { + "type": "keyword" + } }, "context:defaultSize": { "type": "long" @@ -4209,7 +4218,10 @@ "type": "long" }, "context:tieBreakerFields": { - "type": "keyword" + "type": "array", + "items": { + "type": "keyword" + } }, "discover:sort:defaultOrder": { "type": "keyword" @@ -4275,7 +4287,10 @@ "type": "boolean" }, "metaFields": { - "type": "keyword" + "type": "array", + "items": { + "type": "keyword" + } }, "indexPattern:placeholder": { "type": "keyword" @@ -4339,6 +4354,36 @@ }, "dateFormat": { "type": "keyword" + }, + "autocomplete:useTimeRange": { + "type": "boolean" + }, + "search:timeout": { + "type": "long" + }, + "visualization:visualize:legacyChartsLibrary": { + "type": "boolean" + }, + "doc_table:legacy": { + "type": "boolean" + }, + "discover:modifyColumnsOnSwitch": { + "type": "boolean" + }, + "discover:searchFieldsFromSource": { + "type": "boolean" + }, + "securitySolution:rulesTableRefresh": { + "type": "text" + }, + "apm:enableSignificantTerms": { + "type": "boolean" + }, + "apm:enableServiceOverview": { + "type": "boolean" + }, + "apm:enableCorrelations": { + "type": "boolean" } } }, diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index ccc17ea1c5967..8e8a74902d479 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -14,17 +14,38 @@ import { KibanaRequest, } from 'src/core/server'; -export type AllowedSchemaNumberTypes = 'long' | 'integer' | 'short' | 'byte' | 'double' | 'float'; +export type AllowedSchemaNumberTypes = + | 'long' + | 'integer' + | 'short' + | 'byte' + | 'double' + | 'float' + | 'date'; +export type AllowedSchemaStringTypes = 'keyword' | 'text' | 'date'; +export type AllowedSchemaBooleanTypes = 'boolean'; -export type AllowedSchemaTypes = AllowedSchemaNumberTypes | 'keyword' | 'text' | 'boolean' | 'date'; +export type AllowedSchemaTypes = + | AllowedSchemaNumberTypes + | AllowedSchemaStringTypes + | AllowedSchemaBooleanTypes; export interface SchemaField { type: string; } +export type PossibleSchemaTypes = U extends string + ? AllowedSchemaStringTypes + : U extends number + ? AllowedSchemaNumberTypes + : U extends boolean + ? AllowedSchemaBooleanTypes + : // allow any schema type from the union if typescript is unable to resolve the exact U type + AllowedSchemaTypes; + export type RecursiveMakeSchemaFrom = U extends object ? MakeSchemaFrom - : { type: AllowedSchemaTypes }; + : { type: PossibleSchemaTypes }; // Using Required to enforce all optional keys in the object export type MakeSchemaFrom = { diff --git a/src/plugins/vis_type_timelion/server/plugin.ts b/src/plugins/vis_type_timelion/server/plugin.ts index f999c1dfc773a..fca557efc01e3 100644 --- a/src/plugins/vis_type_timelion/server/plugin.ts +++ b/src/plugins/vis_type_timelion/server/plugin.ts @@ -173,6 +173,7 @@ export class Plugin { defaultMessage: '{experimentalLabel} Your API key from www.quandl.com', values: { experimentalLabel: `[${experimentalLabel}]` }, }), + sensitive: true, category: ['timelion'], schema: schema.string(), }, diff --git a/x-pack/plugins/dashboard_mode/server/ui_settings.ts b/x-pack/plugins/dashboard_mode/server/ui_settings.ts index f692ec8a33fc9..59de82cf7b3ab 100644 --- a/x-pack/plugins/dashboard_mode/server/ui_settings.ts +++ b/x-pack/plugins/dashboard_mode/server/ui_settings.ts @@ -22,6 +22,7 @@ export function getUiSettings(): Record> { }), value: [DASHBOARD_ONLY_USER_ROLE], category: ['dashboard'], + sensitive: true, deprecation: { message: i18n.translate('xpack.dashboardMode.uiSettings.dashboardsOnlyRolesDeprecation', { defaultMessage: 'This setting is deprecated and will be removed in Kibana 8.0.', diff --git a/x-pack/plugins/reporting/server/plugin.ts b/x-pack/plugins/reporting/server/plugin.ts index 05556f050e213..35101dbaab246 100644 --- a/x-pack/plugins/reporting/server/plugin.ts +++ b/x-pack/plugins/reporting/server/plugin.ts @@ -52,6 +52,7 @@ export class ReportingPlugin description: i18n.translate('xpack.reporting.pdfFooterImageDescription', { defaultMessage: `Custom image to use in the PDF's footer`, }), + sensitive: true, type: 'image', schema: schema.nullable(schema.byteSize({ max: '200kb' })), category: [PLUGIN_ID], diff --git a/x-pack/plugins/security_solution/server/ui_settings.ts b/x-pack/plugins/security_solution/server/ui_settings.ts index 548f718e1bc80..0d679cdefb92c 100644 --- a/x-pack/plugins/security_solution/server/ui_settings.ts +++ b/x-pack/plugins/security_solution/server/ui_settings.ts @@ -78,6 +78,8 @@ export const initUiSettings = (uiSettings: CoreSetup['uiSettings']) => { name: i18n.translate('xpack.securitySolution.uiSettings.defaultIndexLabel', { defaultMessage: 'Elasticsearch indices', }), + sensitive: true, + value: DEFAULT_INDEX_PATTERN, description: i18n.translate('xpack.securitySolution.uiSettings.defaultIndexDescription', { defaultMessage: @@ -147,6 +149,7 @@ export const initUiSettings = (uiSettings: CoreSetup['uiSettings']) => { defaultMessage: 'News feed URL', }), value: NEWS_FEED_URL_SETTING_DEFAULT, + sensitive: true, description: i18n.translate('xpack.securitySolution.uiSettings.newsFeedUrlDescription', { defaultMessage: '

News feed content will be retrieved from this URL

', }), @@ -167,6 +170,7 @@ export const initUiSettings = (uiSettings: CoreSetup['uiSettings']) => { 'Array of URL templates to build the list of reputation URLs to be displayed on the IP Details page.', } ), + sensitive: true, category: [APP_ID], requiresPageReload: true, schema: schema.arrayOf( diff --git a/x-pack/test/usage_collection/config.ts b/x-pack/test/usage_collection/config.ts index 27b12a1ff298c..d31ecc444d00d 100644 --- a/x-pack/test/usage_collection/config.ts +++ b/x-pack/test/usage_collection/config.ts @@ -24,7 +24,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { return { ...xpackFunctionalConfig.getAll(), // list paths to the files that contain your plugins tests - testFiles: [resolve(__dirname, './test_suites/application_usage')], + testFiles: [ + resolve(__dirname, './test_suites/application_usage'), + resolve(__dirname, './test_suites/stack_management_usage'), + ], services, pageObjects, diff --git a/x-pack/test/usage_collection/plugins/stack_management_usage_test/kibana.json b/x-pack/test/usage_collection/plugins/stack_management_usage_test/kibana.json new file mode 100644 index 0000000000000..b586de3fa4d79 --- /dev/null +++ b/x-pack/test/usage_collection/plugins/stack_management_usage_test/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "StackManagementUsageTest", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack", "StackManagementUsageTest"], + "requiredPlugins": [], + "server": false, + "ui": true +} diff --git a/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/index.ts b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/index.ts new file mode 100644 index 0000000000000..82aae6988052a --- /dev/null +++ b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { StackManagementUsageTest } from './plugin'; + +export function plugin() { + return new StackManagementUsageTest(); +} diff --git a/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/plugin.ts b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/plugin.ts new file mode 100644 index 0000000000000..3cd10a1d4c178 --- /dev/null +++ b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/plugin.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Plugin, CoreSetup, CoreStart } from 'kibana/public'; +import './types'; + +export class StackManagementUsageTest implements Plugin { + public setup(core: CoreSetup) {} + + public start(core: CoreStart) { + const allUiSettings = core.uiSettings.getAll(); + window.__registeredUiSettings__ = allUiSettings; + } +} diff --git a/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/types.ts b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/types.ts new file mode 100644 index 0000000000000..c49ec89d94b9f --- /dev/null +++ b/x-pack/test/usage_collection/plugins/stack_management_usage_test/public/types.ts @@ -0,0 +1,14 @@ +/* + * 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 { IUiSettingsClient } from 'src/core/public'; +export {}; // Hack to declare this file as a module so TS allows us to extend the Global Window interface + +declare global { + interface Window { + __registeredUiSettings__: ReturnType; + } +} diff --git a/x-pack/test/usage_collection/plugins/stack_management_usage_test/tsconfig.json b/x-pack/test/usage_collection/plugins/stack_management_usage_test/tsconfig.json new file mode 100644 index 0000000000000..f1bf94a38de8f --- /dev/null +++ b/x-pack/test/usage_collection/plugins/stack_management_usage_test/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "public/**/*.ts", + "public/**/*.tsx", + ], + "exclude": [] +} diff --git a/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts b/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts new file mode 100644 index 0000000000000..b8f0cf522605e --- /dev/null +++ b/x-pack/test/usage_collection/test_suites/stack_management_usage/index.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { stackManagementSchema } from '../../../../../src/plugins/kibana_usage_collection/server/collectors/management/schema'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + describe('Stack Management', function () { + this.tags('ciGroup1'); + const { common } = getPageObjects(['common']); + const browser = getService('browser'); + + let registeredSettings: Record; + + before(async () => { + await common.navigateToApp('home'); // Navigate to Home to make sure all the appIds are loaded + registeredSettings = await browser.execute(() => window.__registeredUiSettings__); + }); + + it('registers all UI Settings in the UsageStats interface', () => { + const unreportedUISettings = Object.keys(registeredSettings) + .filter((key) => key !== 'buildNum') + .filter((key) => typeof _.get(stackManagementSchema, key) === 'undefined'); + + if (unreportedUISettings.length) { + throw new Error( + `Detected the following unregistered UI Settings in the stack management collector: + ${JSON.stringify(unreportedUISettings, null)} + Update the management collector schema and its UsageStats interface. + Refer to src/plugins/kibana_usage_collection/server/collectors/management/README.md for additional information. + ` + ); + } + }); + + it('registers all sensitive UI settings as keyword type', async () => { + const sensitiveSettings = Object.entries(registeredSettings) + .filter(([, config]) => config.sensitive) + .map(([key]) => key); + + const nonBooleanSensitiveProps = sensitiveSettings + .map((key) => ({ key, ..._.get(stackManagementSchema, key) })) + .filter((keyDescriptor) => keyDescriptor.type !== 'keyword'); + + if (nonBooleanSensitiveProps.length) { + throw new Error( + `Detected the following sensitive UI Settings in the stack management collector not having a 'keyword' type: + ${JSON.stringify(nonBooleanSensitiveProps, null)} + Update each setting in the management collector schema with ({ type: 'keyword' }). + Refer to src/plugins/kibana_usage_collection/server/collectors/management/README.md for additional information. + ` + ); + } + }); + }); +}