From d6f9adf73adb54905c68e7fec82d0780a18ebad1 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 27 Oct 2021 16:29:41 +0000 Subject: [PATCH 01/22] skip flaky suite (#116463) --- .../index_pattern_field_editor_example.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts index 5744c8e64f5c1..fa4308ae72883 100644 --- a/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts +++ b/test/examples/index_pattern_field_editor_example/index_pattern_field_editor_example.ts @@ -12,7 +12,8 @@ import { PluginFunctionalProviderContext } from 'test/plugin_functional/services export default function ({ getService }: PluginFunctionalProviderContext) { const testSubjects = getService('testSubjects'); - describe('', () => { + // FAILING: https://github.com/elastic/kibana/issues/116463 + describe.skip('', () => { it('finds an index pattern', async () => { await testSubjects.existOrFail('indexPatternTitle'); }); From e0fe09607f0604d3d69c2876e9323b3d37ef2d12 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Wed, 27 Oct 2021 19:11:08 +0200 Subject: [PATCH 02/22] Unskip migrations actions integration tests (#116285) --- .../actions/integration_tests/actions.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts index 3ca3a8505338b..8cf49440f76b8 100644 --- a/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts +++ b/src/core/server/saved_objects/migrationsv2/actions/integration_tests/actions.test.ts @@ -55,8 +55,7 @@ const { startES } = kbnTestServer.createTestServers({ }); let esServer: kbnTestServer.TestElasticsearchUtils; -// Failing: See https://github.com/elastic/kibana/issues/113697 -describe.skip('migration actions', () => { +describe('migration actions', () => { let client: ElasticsearchClient; beforeAll(async () => { @@ -1158,7 +1157,7 @@ describe.skip('migration actions', () => { it('resolves left wait_for_task_completion_timeout when the task does not complete within the timeout', async () => { const res = (await pickupUpdatedMappings( client, - 'existing_index_with_docs' + '.kibana_1' )()) as Either.Right; const task = waitForPickupUpdatedMappingsTask({ @@ -1539,7 +1538,8 @@ describe.skip('migration actions', () => { } `); }); - it('resolves left request_entity_too_large_exception when the payload is too large', async () => { + // TODO: unskip after https://github.com/elastic/kibana/issues/116111 + it.skip('resolves left request_entity_too_large_exception when the payload is too large', async () => { const newDocs = new Array(10000).fill({ _source: { title: From 6b4d9dd1a9dc3720334ce4eaa7f6085f4bd7f656 Mon Sep 17 00:00:00 2001 From: Nick Partridge Date: Wed, 27 Oct 2021 12:22:35 -0500 Subject: [PATCH 03/22] [Charts] Multi-layer time axis (#115853) This commit introduces the multi-layer time axis in Discover, Lens, Visualize, TSVB. It adds visualization:useLegacyTimeAxis advanced settings under charts plugin to toggle legacy time axis. The new multi-layer time axis is introduced in @elastic/charts https://elastic.github.io/elastic-charts/?path=/story/area-chart--timeslip and was demoed as part of the Kibana Demo Days. It is the outcome of the research done in elastic/elastic-charts#1310 related to improving the time axis solving the following problems: - sparse time labels that can be far apart - unclear where time point is on the label (the middle) - difficult / tedious to read due to redundant information and small fonts - resolution is not explicit (is it hours of days or days themselves) --- docs/management/advanced-options.asciidoc | 3 + package.json | 2 +- src/plugins/charts/common/index.ts | 1 + src/plugins/charts/server/plugin.ts | 17 ++- .../main/components/chart/discover_chart.tsx | 2 - .../apps/main/components/chart/histogram.tsx | 55 ++++++++- .../server/collectors/management/schema.ts | 4 + .../server/collectors/management/types.ts | 1 + src/plugins/telemetry/schema/oss_plugins.json | 6 + .../components/vis_types/timeseries/vis.js | 2 + .../visualizations/views/timeseries/index.js | 54 ++++++++- .../xy/public/components/xy_axis.tsx | 2 + .../vis_types/xy/public/config/get_axis.ts | 66 +++++++++-- .../vis_types/xy/public/config/get_config.ts | 38 +++++-- src/plugins/vis_types/xy/public/plugin.ts | 8 +- .../vis_types/xy/public/types/config.ts | 1 + .../vis_types/xy/public/vis_component.tsx | 6 +- .../vis_types/xy/public/vis_renderer.tsx | 9 +- .../apps/dashboard/dashboard_snapshots.ts | 4 +- test/functional/apps/visualize/_timelion.ts | 2 +- test/functional/config.js | 1 + .../__snapshots__/expression.test.tsx.snap | 7 ++ .../xy_visualization/expression.test.tsx | 1 + .../public/xy_visualization/expression.tsx | 104 +++++++++++++----- .../lens/public/xy_visualization/index.ts | 2 + yarn.lock | 14 ++- 26 files changed, 341 insertions(+), 71 deletions(-) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 6bac5e7940dbb..f2da704760f3b 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -516,6 +516,9 @@ Enables the legacy charts library for timelion charts in Visualize. **This setting is deprecated and will not be supported as of 8.0.** Maps values to specific colors in charts using the *Compatibility* palette. +[[visualization-uselegacytimeaxis]]`visualization:useLegacyTimeAxis`:: +Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB + [[visualization-heatmap-maxbuckets]]`visualization:heatmap:maxBuckets`:: The maximum number of buckets a datasource can return. High numbers can have a negative impact on your browser rendering performance. diff --git a/package.json b/package.json index b2065ddc1d608..3c42c077ae5b0 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/apm-rum": "^5.9.1", "@elastic/apm-rum-react": "^1.3.1", - "@elastic/charts": "38.0.1", + "@elastic/charts": "38.1.0", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.35", "@elastic/ems-client": "8.0.0", diff --git a/src/plugins/charts/common/index.ts b/src/plugins/charts/common/index.ts index 618466212f5bb..d06dbc73e29b1 100644 --- a/src/plugins/charts/common/index.ts +++ b/src/plugins/charts/common/index.ts @@ -7,6 +7,7 @@ */ export const COLOR_MAPPING_SETTING = 'visualization:colorMapping'; +export const LEGACY_TIME_AXIS = 'visualization:useLegacyTimeAxis'; export { CustomPaletteArguments, diff --git a/src/plugins/charts/server/plugin.ts b/src/plugins/charts/server/plugin.ts index 63b703e6b7538..c7559b525cd22 100644 --- a/src/plugins/charts/server/plugin.ts +++ b/src/plugins/charts/server/plugin.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { schema } from '@kbn/config-schema'; import { CoreSetup, Plugin } from 'kibana/server'; -import { COLOR_MAPPING_SETTING, palette, systemPalette } from '../common'; +import { COLOR_MAPPING_SETTING, LEGACY_TIME_AXIS, palette, systemPalette } from '../common'; import { ExpressionsServerSetup } from '../../expressions/server'; interface SetupDependencies { @@ -45,6 +45,21 @@ export class ChartsServerPlugin implements Plugin { category: ['visualization'], schema: schema.string(), }, + [LEGACY_TIME_AXIS]: { + name: i18n.translate('charts.advancedSettings.visualization.useLegacyTimeAxis.name', { + defaultMessage: 'Legacy chart time axis', + }), + value: false, + description: i18n.translate( + 'charts.advancedSettings.visualization.useLegacyTimeAxis.description', + { + defaultMessage: + 'Enables the legacy time axis for charts in Lens, Discover, Visualize and TSVB', + } + ), + category: ['visualization'], + schema: schema.boolean(), + }, }); return {}; diff --git a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx index 166c2272a00f4..1fe149f3eb17d 100644 --- a/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx +++ b/src/plugins/discover/public/application/apps/main/components/chart/discover_chart.tsx @@ -115,7 +115,6 @@ export function DiscoverChart({ onResetQuery={resetSavedSearch} /> - {showViewModeToggle && ( )} - {timefield && ( = useLegacyTimeAxis + ? {} + : { strokeWidth: 0.1, stroke: isDarkMode ? 'white' : 'black' }; + const verticalAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + visible: false, + }, + tickLabel: { + fontSize: 11, + }, + }; + const xAxisStyle: RecursivePartial = useLegacyTimeAxis + ? {} + : { + axisLine: { + stroke: isDarkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: isDarkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + tickLabel: { + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + }; + return (
@@ -195,15 +241,19 @@ export function DiscoverHistogram({ xAxisFormatter.convert(value)} + gridLine={gridLineStyle} + style={verticalAxisStyle} /> 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 007d3a99cb1dd..001efe0433dc8 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -64,6 +64,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'text', _meta: { description: 'Non-default value of setting.' }, }, + 'visualization:useLegacyTimeAxis': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'visualization:regionmap:showWarnings': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index d35a05fe04780..53d651b022529 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -40,6 +40,7 @@ export interface UsageStats { 'visualize:enableLabs': boolean; 'visualization:heatmap:maxBuckets': number; 'visualization:colorMapping': string; + 'visualization:useLegacyTimeAxis': boolean; 'visualization:regionmap:showWarnings': boolean; 'visualization:tileMap:maxPrecision': number; 'csv:separator': string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 138ce3f097ce9..e7c18966ce0c0 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7110,6 +7110,12 @@ "description": "Non-default value of setting." } }, + "visualization:useLegacyTimeAxis": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "visualization:regionmap:showWarnings": { "type": "boolean", "_meta": { diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js index b4fe39c522de7..b177ef632e210 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.js @@ -19,6 +19,7 @@ import { createFieldFormatter } from '../../lib/create_field_formatter'; import { checkIfSeriesHaveSameFormatters } from '../../lib/check_if_series_have_same_formatters'; import { TimeSeries } from '../../../visualizations/views/timeseries'; import { MarkdownSimple } from '../../../../../../../../plugins/kibana_react/public'; +import { LEGACY_TIME_AXIS } from '../../../../../../../../plugins/charts/common'; import { replaceVars } from '../../lib/replace_vars'; import { getInterval } from '../../lib/get_interval'; import { createIntervalBasedFormatter } from '../../lib/create_interval_based_formatter'; @@ -272,6 +273,7 @@ class TimeseriesVisualization extends Component { syncColors={syncColors} palettesService={palettesService} interval={interval} + useLegacyTimeAxis={getConfig(LEGACY_TIME_AXIS, false)} isLastBucketDropped={Boolean( model.drop_last_bucket || model.series.some((series) => series.series_drop_last_bucket) diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 2158283bb80d5..5e98b74c0caa5 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -72,6 +72,7 @@ export const TimeSeries = ({ palettesService, interval, isLastBucketDropped, + useLegacyTimeAxis, }) => { // If the color isn't configured by the user, use the color mapping service // to assign a color from the Kibana palette. Colors will be shared across the @@ -138,6 +139,51 @@ export const TimeSeries = ({ }, [palettesService, series, syncColors] ); + + const darkMode = uiSettings.get('theme:darkMode'); + const gridLineStyle = !useLegacyTimeAxis + ? { + visible: showGrid, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + ...GRID_LINE_CONFIG, + visible: showGrid, + }; + const xAxisStyle = !useLegacyTimeAxis + ? { + tickLabel: { + visible: true, + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: true, + }, + axisTitle: { + visible: true, + padding: 0, + }, + } + : {}; + return ( ); @@ -357,4 +402,5 @@ TimeSeries.propTypes = { annotations: PropTypes.array, interval: PropTypes.number, isLastBucketDropped: PropTypes.bool, + useLegacyTimeAxis: PropTypes.bool, }; diff --git a/src/plugins/vis_types/xy/public/components/xy_axis.tsx b/src/plugins/vis_types/xy/public/components/xy_axis.tsx index 30e1dbbff673e..b224639bdbff3 100644 --- a/src/plugins/vis_types/xy/public/components/xy_axis.tsx +++ b/src/plugins/vis_types/xy/public/components/xy_axis.tsx @@ -25,6 +25,7 @@ export const XYAxis: FC = ({ domain, style, integersOnly, + timeAxisLayerCount, }) => ( = ({ labelFormat={ticks?.labelFormatter} showOverlappingLabels={ticks?.showOverlappingLabels} showDuplicatedTicks={ticks?.showDuplicates} + timeAxisLayerCount={timeAxisLayerCount} /> ); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index 0d6c67d064cf8..796636ef2cb61 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -8,7 +8,13 @@ import { identity } from 'lodash'; -import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; +import { + AxisSpec, + TickFormatter, + YDomainRange, + ScaleType as ECScaleType, + Position, +} from '@elastic/charts'; import { LabelRotation } from '../../../../charts/public'; import { BUCKET_TYPES } from '../../../../data/public'; @@ -33,7 +39,9 @@ export function getAxis( { categoryLines, valueAxis }: Grid, { params, format, formatter, title: fallbackTitle = '', aggType }: Aspect, seriesParams: SeriesParam[], - isDateHistogram = false + isDateHistogram = false, + useMultiLayerTimeAxis = false, + darkMode = false ): AxisConfig { const isCategoryAxis = type === AxisType.Category; // Hide unassigned axis, not supported in elastic charts @@ -74,9 +82,10 @@ export function getAxis( ticks, grid, scale, - style: getAxisStyle(ticks, title, fallbackRotation), + style: getAxisStyle(useMultiLayerTimeAxis, darkMode, ticks, title, fallbackRotation), domain: getAxisDomain(scale, isCategoryAxis), integersOnly: aggType === 'count', + timeAxisLayerCount: useMultiLayerTimeAxis ? 3 : 0, }; } @@ -147,19 +156,52 @@ export function getScale( } function getAxisStyle( + isMultiLayerTimeAxis: boolean, + darkMode: boolean, ticks?: TickOptions, title?: string, rotationFallback: LabelRotation = LabelRotation.Vertical ): AxisSpec['style'] { - return { - axisTitle: { - visible: (title ?? '').trim().length > 0, - }, - tickLabel: { - visible: ticks?.show, - rotation: -(ticks?.rotation ?? rotationFallback), - }, - }; + return isMultiLayerTimeAxis + ? { + tickLabel: { + visible: Boolean(ticks?.show), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(ticks?.show), + }, + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + } + : { + axisTitle: { + visible: (title ?? '').trim().length > 0, + }, + tickLabel: { + visible: ticks?.show, + rotation: -(ticks?.rotation ?? rotationFallback), + }, + }; } function getAxisDomain( diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index d2a3b9ad2a103..bd79b915be917 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -29,7 +29,12 @@ import { getAxis } from './get_axis'; import { getAspects } from './get_aspects'; import { ChartType } from '../index'; -export function getConfig(table: Datatable, params: VisParams): VisConfig { +export function getConfig( + table: Datatable, + params: VisParams, + useLegacyTimeAxis = false, + darkMode = false +): VisConfig { const { thresholdLine, orderBucketsBySum, @@ -42,13 +47,6 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { fillOpacity, } = params; const aspects = getAspects(table.columns, params.dimensions); - const xAxis = getAxis( - params.categoryAxes[0], - params.grid, - aspects.x, - params.seriesParams, - params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM - ); const tooltip = getTooltip(aspects, params); const yAxes = params.valueAxes.map((a) => { // find the correct aspect for each value axis @@ -60,10 +58,28 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { params.seriesParams ); }); + + const rotation = getRotation(params.categoryAxes[0]); + + const isDateHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM; + const isHistogram = params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM; const enableHistogramMode = - (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || - params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && + (isDateHistogram || isHistogram) && shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); + + const useMultiLayerTimeAxis = + enableHistogramMode && isDateHistogram && !useLegacyTimeAxis && rotation === 0; + + const xAxis = getAxis( + params.categoryAxes[0], + params.grid, + aspects.x, + params.seriesParams, + isDateHistogram, + useMultiLayerTimeAxis, + darkMode + ); + const isTimeChart = (aspects.x.params as DateHistogramParams).date ?? false; return { @@ -83,7 +99,7 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { xAxis, yAxes, legend: getLegend(params), - rotation: getRotation(params.categoryAxes[0]), + rotation, thresholdLine: getThresholdLine(thresholdLine, yAxes, params.seriesParams), }; } diff --git a/src/plugins/vis_types/xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts index c79ead242e35b..0f1de387161e3 100644 --- a/src/plugins/vis_types/xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -24,7 +24,7 @@ import { } from './services'; import { visTypesDefinitions } from './vis_types'; -import { xyVisRenderer } from './vis_renderer'; +import { getXYVisRenderer } from './vis_renderer'; import * as expressionFunctions from './expression_functions'; @@ -69,7 +69,11 @@ export class VisTypeXyPlugin setThemeService(charts.theme); setPalettesService(charts.palettes); - expressions.registerRenderer(xyVisRenderer); + expressions.registerRenderer( + getXYVisRenderer({ + uiSettings: core.uiSettings, + }) + ); expressions.registerFunction(expressionFunctions.visTypeXyVisFn); expressions.registerFunction(expressionFunctions.categoryAxis); expressions.registerFunction(expressionFunctions.timeMarker); diff --git a/src/plugins/vis_types/xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts index e52b47366bc85..287787193bd20 100644 --- a/src/plugins/vis_types/xy/public/types/config.ts +++ b/src/plugins/vis_types/xy/public/types/config.ts @@ -85,6 +85,7 @@ export interface AxisConfig { title?: string; grid?: AxisGrid; integersOnly: boolean; + timeAxisLayerCount?: number; } export interface LegendOptions { diff --git a/src/plugins/vis_types/xy/public/vis_component.tsx b/src/plugins/vis_types/xy/public/vis_component.tsx index 515ad3e7eaf6f..8574e86a23096 100644 --- a/src/plugins/vis_types/xy/public/vis_component.tsx +++ b/src/plugins/vis_types/xy/public/vis_component.tsx @@ -66,6 +66,7 @@ export interface VisComponentProps { fireEvent: IInterpreterRenderHandlers['event']; renderComplete: IInterpreterRenderHandlers['done']; syncColors: boolean; + useLegacyTimeAxis: boolean; } export type VisComponentType = typeof VisComponent; @@ -211,8 +212,9 @@ const VisComponent = (props: VisComponentProps) => { ); const { visData, visParams, syncColors } = props; + const isDarkMode = getThemeService().useDarkMode(); - const config = getConfig(visData, visParams); + const config = getConfig(visData, visParams, props.useLegacyTimeAxis, isDarkMode); const timeZone = getTimeZone(); const xDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getXDomain(config.aspects.x.params); @@ -229,7 +231,7 @@ const VisComponent = (props: VisComponentProps) => { () => config.legend.position ?? Position.Right, [config.legend.position] ); - const isDarkMode = getThemeService().useDarkMode(); + const getSeriesName = getSeriesNameFn(config.aspects, config.aspects.y.length > 1); const splitAccessors = config.aspects.series?.map(({ accessor, formatter }) => { diff --git a/src/plugins/vis_types/xy/public/vis_renderer.tsx b/src/plugins/vis_types/xy/public/vis_renderer.tsx index 093671307d538..77727761015a7 100644 --- a/src/plugins/vis_types/xy/public/vis_renderer.tsx +++ b/src/plugins/vis_types/xy/public/vis_renderer.tsx @@ -9,6 +9,7 @@ import React, { lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; +import { IUiSettingsClient } from 'kibana/public'; import { VisualizationContainer } from '../../../visualizations/public'; import type { PersistedState } from '../../../visualizations/public'; @@ -17,6 +18,7 @@ import type { ExpressionRenderDefinition } from '../../../expressions/public'; import type { XyVisType } from '../common'; import type { VisComponentType } from './vis_component'; import { RenderValue, visName } from './expression_functions/xy_vis_fn'; +import { LEGACY_TIME_AXIS } from '../../../charts/common'; // @ts-ignore const VisComponent = lazy(() => import('./vis_component')); @@ -28,7 +30,9 @@ function shouldShowNoResultsMessage(visData: any, visType: XyVisType): boolean { return Boolean(isZeroHits); } -export const xyVisRenderer: ExpressionRenderDefinition = { +export const getXYVisRenderer: (deps: { + uiSettings: IUiSettingsClient; +}) => ExpressionRenderDefinition = ({ uiSettings }) => ({ name: visName, displayName: 'XY visualization', reuseDomNode: true, @@ -46,10 +50,11 @@ export const xyVisRenderer: ExpressionRenderDefinition = { fireEvent={handlers.event} uiState={handlers.uiState as PersistedState} syncColors={syncColors} + useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)} /> , domNode ); }, -}; +}); diff --git a/test/functional/apps/dashboard/dashboard_snapshots.ts b/test/functional/apps/dashboard/dashboard_snapshots.ts index 3aba671c0a4b2..9279bbd5806e7 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.ts +++ b/test/functional/apps/dashboard/dashboard_snapshots.ts @@ -59,7 +59,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); it('compare area chart snapshot', async () => { @@ -81,7 +81,7 @@ export default function ({ ); await PageObjects.dashboard.clickExitFullScreenLogoButton(); - expect(percentDifference).to.be.lessThan(0.02); + expect(percentDifference).to.be.lessThan(0.022); }); }); } diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index c531ada8a2573..712edb440311f 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -167,7 +167,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(firstAreaChartData).to.eql(firstAreaExpectedChartData); expect(secondAreaChartData).to.eql(secondAreaExpectedChartData); expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData); - expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']); + expect(firstAxesLabels).to.eql(['12.2GB', '12.21GB']); expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']); expect(thirdAxesLabels.toString()).to.be( 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733' diff --git a/test/functional/config.js b/test/functional/config.js index 5b0b79e84e8df..09eccc863a0e5 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -54,6 +54,7 @@ export default async function ({ readConfigFile }) { 'accessibility:disableAnimations': true, 'dateFormat:tz': 'UTC', 'visualization:visualize:legacyPieChartsLibrary': true, + 'visualization:useLegacyTimeAxis': true, }, }, diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap index b058c42d8b4d1..6601167e1f83a 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/expression.test.tsx.snap @@ -67,6 +67,7 @@ exports[`xy_expression XYChart component it renders area 1`] = ` } } tickFormat={[Function]} + timeAxisLayerCount={0} title="c" /> { onClickValue, onSelectRange, syncColors: false, + useLegacyTimeAxis: false, }; }); diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index a3022e830c861..80e4a8d4dc8f7 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -31,6 +31,8 @@ import { LabelOverflowConstraint, DisplayValueStyle, RecursivePartial, + AxisStyle, + GridLineStyle, ScaleType, } from '@elastic/charts'; import { I18nProvider } from '@kbn/i18n/react'; @@ -90,6 +92,7 @@ export type XYChartRenderProps = XYChartProps & { paletteService: PaletteRegistry; formatFactory: FormatFactory; timeZone: string; + useLegacyTimeAxis: boolean; minInterval: number | undefined; interactive?: boolean; onClickValue: (data: LensFilterEvent['data']) => void; @@ -130,6 +133,7 @@ export const getXyChartRenderer = (dependencies: { chartsActiveCursorService: ChartsPluginStart['activeCursor']; paletteService: PaletteRegistry; timeZone: string; + useLegacyTimeAxis: boolean; }): ExpressionRenderDefinition => ({ name: 'lens_xy_chart_renderer', displayName: 'XY chart', @@ -160,6 +164,7 @@ export const getXyChartRenderer = (dependencies: { chartsThemeService={dependencies.chartsThemeService} paletteService={dependencies.paletteService} timeZone={dependencies.timeZone} + useLegacyTimeAxis={dependencies.useLegacyTimeAxis} minInterval={calculateMinInterval(config)} interactive={handlers.isInteractive()} onClickValue={onClickValue} @@ -235,6 +240,7 @@ export function XYChart({ onSelectRange, interactive = true, syncColors, + useLegacyTimeAxis, }: XYChartRenderProps) { const { legend, @@ -320,15 +326,15 @@ export function XYChart({ filteredBarLayers.some((layer) => layer.accessors.length > 1) || filteredBarLayers.some((layer) => layer.splitAccessor); - const isTimeViz = data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time'); + const isTimeViz = Boolean(data.dateRange && filteredLayers.every((l) => l.xScaleType === 'time')); const isHistogramViz = filteredLayers.every((l) => l.isHistogram); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( filteredLayers, data, minInterval, - Boolean(isTimeViz), - Boolean(isHistogramViz) + isTimeViz, + isHistogramViz ); const yAxesMap = { @@ -555,6 +561,72 @@ export function XYChart({ floatingColumns: legend?.floatingColumns ?? 1, } as LegendPositionConfig; + // todo be moved in the chart plugin + const shouldUseNewTimeAxis = isTimeViz && !useLegacyTimeAxis && !shouldRotate; + + const gridLineStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 0.1, + stroke: darkMode ? 'white' : 'black', + } + : { + visible: gridlinesVisibilitySettings?.x, + strokeWidth: 2, + }; + const xAxisStyle: RecursivePartial = shouldUseNewTimeAxis + ? { + tickLabel: { + visible: Boolean(tickLabelsVisibilitySettings?.x), + rotation: 0, // rotation is disabled on new time axis + fontSize: 11, + padding: + referenceLinePaddings.bottom != null ? { inner: referenceLinePaddings.bottom } : 0, + alignment: { + vertical: Position.Bottom, + horizontal: Position.Left, + }, + offset: { + x: 1.5, + y: 0, + }, + }, + axisLine: { + stroke: darkMode ? 'lightgray' : 'darkgray', + strokeWidth: 1, + }, + tickLine: { + size: 12, + strokeWidth: 0.15, + stroke: darkMode ? 'white' : 'black', + padding: -10, + visible: Boolean(tickLabelsVisibilitySettings?.x), + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + } + : { + tickLabel: { + visible: tickLabelsVisibilitySettings?.x, + rotation: labelsOrientation?.x, + padding: + referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + axisTitle: { + visible: axisTitlesVisibilitySettings.x, + padding: + !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null + ? { inner: referenceLinePaddings.bottom } + : undefined, + }, + }; return ( safeXAccessorLabelRenderer(d.value), }} - allowBrushingLastHistogramBin={Boolean(isTimeViz)} + allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} @@ -618,29 +690,11 @@ export function XYChart({ id="x" position={shouldRotate ? Position.Left : Position.Bottom} title={xTitle} - gridLine={{ - visible: gridlinesVisibilitySettings?.x, - strokeWidth: 2, - }} + gridLine={gridLineStyle} hide={filteredLayers[0].hide || !filteredLayers[0].xAccessor} tickFormat={(d) => safeXAccessorLabelRenderer(d)} - style={{ - tickLabel: { - visible: tickLabelsVisibilitySettings?.x, - rotation: labelsOrientation?.x, - padding: - referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - axisTitle: { - visible: axisTitlesVisibilitySettings.x, - padding: - !tickLabelsVisibilitySettings?.x && referenceLinePaddings.bottom != null - ? { inner: referenceLinePaddings.bottom } - : undefined, - }, - }} + style={xAxisStyle} + timeAxisLayerCount={shouldUseNewTimeAxis ? 3 : 0} /> {yAxesConfiguration.map((axis) => { diff --git a/x-pack/plugins/lens/public/xy_visualization/index.ts b/x-pack/plugins/lens/public/xy_visualization/index.ts index f9d48ffaaae37..d3d461e8f8741 100644 --- a/x-pack/plugins/lens/public/xy_visualization/index.ts +++ b/x-pack/plugins/lens/public/xy_visualization/index.ts @@ -12,6 +12,7 @@ import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public import type { LensPluginStartDependencies } from '../plugin'; import { getTimeZone } from '../utils'; import type { FormatFactory } from '../../common'; +import { LEGACY_TIME_AXIS } from '../../../../../src/plugins/charts/common'; export interface XyVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; @@ -37,6 +38,7 @@ export class XyVisualization { chartsActiveCursorService: charts.activeCursor, paletteService: palettes, timeZone: getTimeZone(core.uiSettings), + useLegacyTimeAxis: core.uiSettings.get(LEGACY_TIME_AXIS), }) ); return getXyVisualization({ paletteService: palettes, fieldFormats }); diff --git a/yarn.lock b/yarn.lock index 3e4e3ad4ed224..56d5cf17791ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2337,10 +2337,10 @@ dependencies: object-hash "^1.3.0" -"@elastic/charts@38.0.1": - version "38.0.1" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.0.1.tgz#9c1db7e0f1de869e0b2b505e192bbb9d62d60dc8" - integrity sha512-i9mIA3Ji9jSjuFDtuh9gV1xpCl3sbBEDgJiOgLVt04pr/qZH2W+tr3AV5yHvjsR7Te0Pmh/Cm5wLBvFKaI1nIA== +"@elastic/charts@38.1.0": + version "38.1.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-38.1.0.tgz#52146564c0e399da2267c10ec4536c33e50969e4" + integrity sha512-u2hQ8+daCvqapKQiFqN8QHWTz3OXby5Xf/ta1Dv59KTDTFIinCZD/M8PyD3MapbKx4b67hL7UxbErqr4rZ8jeA== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" @@ -2351,6 +2351,7 @@ d3-interpolate "^1.4.0" d3-scale "^1.0.7" d3-shape "^1.3.4" + luxon "^1.25.0" prop-types "^15.7.2" re-reselect "^3.4.0" react-redux "^7.1.0" @@ -19720,6 +19721,11 @@ lru-queue@0.1: dependencies: es5-ext "~0.10.2" +luxon@^1.25.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" From 017fcac4b9e029b1144171b1dcfcec4ad9d10a3b Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Wed, 27 Oct 2021 10:53:07 -0700 Subject: [PATCH 04/22] Removes functional tests using freeze API (#116373) Signed-off-by: Tyler Smalley --- .../reporting_and_security/index.ts | 1 - .../search_frozen_indices.ts | 127 ------------------ 2 files changed, 128 deletions(-) delete mode 100644 x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts index 159115e2054e1..f6654ff5a6b1d 100644 --- a/x-pack/test/reporting_api_integration/reporting_and_security/index.ts +++ b/x-pack/test/reporting_api_integration/reporting_and_security/index.ts @@ -30,6 +30,5 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./spaces')); loadTestFile(require.resolve('./usage')); loadTestFile(require.resolve('./ilm_migration_apis')); - loadTestFile(require.resolve('./search_frozen_indices')); }); } diff --git a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts b/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts deleted file mode 100644 index daa749649e250..0000000000000 --- a/x-pack/test/reporting_api_integration/reporting_and_security/search_frozen_indices.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function ({ getService }: FtrProviderContext) { - const kibanaServer = getService('kibanaServer'); - const supertestSvc = getService('supertest'); - const esSupertest = getService('esSupertest'); - const indexPatternId = 'cool-test-index-pattern'; - - async function callExportAPI() { - const job = { - browserTimezone: 'UTC', - columns: ['@timestamp', 'ip', 'utilization'], - searchSource: { - fields: [{ field: '*', include_unmapped: 'true' }], - filter: [ - { - meta: { field: '@timestamp', index: indexPatternId, params: {} }, - range: { - '@timestamp': { - format: 'strict_date_optional_time', - gte: '2020-08-24T00:00:00.000Z', - lte: '2022-08-24T21:40:48.346Z', - }, - }, - }, - ], - index: indexPatternId, - parent: { filter: [], index: indexPatternId, query: { language: 'kuery', query: '' } }, - sort: [{ '@timestamp': 'desc' }], - trackTotalHits: true, - }, - title: 'Test search', - }; - - return await supertestSvc - .post(`/api/reporting/v1/generate/immediate/csv_searchsource`) - .set('kbn-xsrf', 'xxx') - .send(job); - } - - describe('Frozen indices search', () => { - const reset = async () => { - await kibanaServer.uiSettings.replace({ 'search:includeFrozen': false }); - try { - await esSupertest.delete('/test1,test2,test3'); - await kibanaServer.savedObjects.delete({ type: 'index-pattern', id: indexPatternId }); - } catch (err) { - // ignore 404 error - } - }; - - before(reset); - after(reset); - - it('Search includes frozen indices based on Advanced Setting', async () => { - await kibanaServer.uiSettings.update({ 'csv:quoteValues': true }); - - // setup: add multiple indices of test data - await Promise.all([ - esSupertest - .post('/test1/_doc') - .send({ '@timestamp': '2021-08-24T21:36:40Z', ip: '43.98.8.183', utilization: 18725 }), - esSupertest - .post('/test2/_doc') - .send({ '@timestamp': '2021-08-21T09:36:40Z', ip: '63.91.103.79', utilization: 8480 }), - esSupertest - .post('/test3/_doc') - .send({ '@timestamp': '2021-08-17T21:36:40Z', ip: '139.108.162.171', utilization: 3078 }), - ]); - await esSupertest.post('/test*/_refresh'); - - // setup: create index pattern - const indexPatternCreateResponse = await kibanaServer.savedObjects.create({ - type: 'index-pattern', - id: indexPatternId, - overwrite: true, - attributes: { title: 'test*', timeFieldName: '@timestamp' }, - }); - expect(indexPatternCreateResponse.id).to.be(indexPatternId); - - // 1. check the initial data with a CSV export - const initialSearch = await callExportAPI(); - expectSnapshot(initialSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - - // 2. freeze an index in the pattern - await esSupertest.post('/test3/_freeze').expect(200); - await esSupertest.post('/test*/_refresh').expect(200); - - // 3. recheck the search results - const afterFreezeSearch = await callExportAPI(); - expectSnapshot(afterFreezeSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - " - `); - - // 4. update setting to allow searching frozen data - await kibanaServer.uiSettings.update({ 'search:includeFrozen': true }); - - // 5. recheck the search results - const afterAllowSearch = await callExportAPI(); - expectSnapshot(afterAllowSearch.text).toMatchInline(` - "\\"@timestamp\\",ip,utilization - \\"Aug 24, 2021 @ 21:36:40.000\\",\\"43.98.8.183\\",\\"18,725\\" - \\"Aug 21, 2021 @ 09:36:40.000\\",\\"63.91.103.79\\",\\"8,480\\" - \\"Aug 17, 2021 @ 21:36:40.000\\",\\"139.108.162.171\\",\\"3,078\\" - " - `); - }); - }); -} From bd2a7cefa55d02ef14356ebb590ba89ffa2f7679 Mon Sep 17 00:00:00 2001 From: Kaarina Tungseth Date: Wed, 27 Oct 2021 13:42:11 -0500 Subject: [PATCH 05/22] [DOCS] Fixes monitoring setting (#116309) --- docs/settings/spaces-settings.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/settings/spaces-settings.asciidoc b/docs/settings/spaces-settings.asciidoc index 5483912387cec..dd37943101145 100644 --- a/docs/settings/spaces-settings.asciidoc +++ b/docs/settings/spaces-settings.asciidoc @@ -13,7 +13,7 @@ return all spaces using a single `_search` from {es}, so you must configure this setting lower than the `index.max_result_window` in {es}. The default is `1000`. -`monitoring.cluster_alerts-allowedSpaces` {ess-icon}:: +`monitoring.cluster_alerts.allowedSpaces` {ess-icon}:: Specifies the spaces where cluster alerts are automatically generated. You must specify all spaces where you want to generate alerts, including the default space. When the default space is unspecified, {kib} is unable to generate an alert for the default space. From 136d322bddce84c34c1192aa917cfce8193a11d5 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Wed, 27 Oct 2021 21:55:55 +0200 Subject: [PATCH 06/22] adds lists plugin to security solution cypress tests execution (#116442) --- .buildkite/scripts/pipelines/pull_request/pipeline.js | 3 +++ vars/tasks.groovy | 1 + 2 files changed, 4 insertions(+) diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.js b/.buildkite/scripts/pipelines/pull_request/pipeline.js index 02d6fc270ddb0..c5ed216042b68 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.js +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.js @@ -57,6 +57,9 @@ const uploadPipeline = (pipelineContent) => { if ( (await doAnyChangesMatch([ /^x-pack\/plugins\/security_solution/, + /^x-pack\/plugins\/cases/, + /^x-pack\/plugins\/lists/, + /^x-pack\/plugins\/timelines/, /^x-pack\/test\/security_solution_cypress/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/sections\/action_connector_form/, /^x-pack\/plugins\/triggers_actions_ui\/public\/application\/context\/actions_connectors_context\.tsx/, diff --git a/vars/tasks.groovy b/vars/tasks.groovy index 0f509fa8ba132..050b62646fb3b 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -137,6 +137,7 @@ def functionalXpack(Map params = [:]) { 'x-pack/plugins/security_solution/', 'x-pack/plugins/cases/', 'x-pack/plugins/timelines/', + 'x-pack/plugins/lists/', 'x-pack/test/security_solution_cypress/', 'x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/', 'x-pack/plugins/triggers_actions_ui/public/application/context/actions_connectors_context.tsx', From c3f207a071c8bc11a4ab4abcf4bf0ed1f8a91c53 Mon Sep 17 00:00:00 2001 From: Byron Hulcher Date: Wed, 27 Oct 2021 16:18:19 -0400 Subject: [PATCH 07/22] Revert "[App Search] Load curation settings at root /curations/ path (#115690)" (#116462) --- .../curations/curations_router.test.tsx | 100 ------------------ .../components/curations/curations_router.tsx | 35 +----- .../components/curations/views/curations.tsx | 12 +-- .../curations_settings.test.tsx | 61 +++++++++++ .../curations_settings/curations_settings.tsx | 26 ++++- 5 files changed, 90 insertions(+), 144 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx index c0278c765e85e..9598212d3e0c9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.test.tsx @@ -5,9 +5,6 @@ * 2.0. */ -import '../../../__mocks__/shallow_useeffect.mock'; -import '../../../__mocks__/react_router'; -import { setMockActions, setMockValues } from '../../../__mocks__/kea_logic'; import '../../__mocks__/engine_logic.mock'; import React from 'react'; @@ -15,110 +12,13 @@ import { Route, Switch } from 'react-router-dom'; import { shallow } from 'enzyme'; -import { LogRetentionOptions } from '../log_retention'; - import { CurationsRouter } from './'; -const MOCK_VALUES = { - // CurationsSettingsLogic - dataLoading: false, - curationsSettings: { - enabled: true, - mode: 'automatic', - }, - // LogRetentionLogic - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - // LicensingLogic - hasPlatinumLicense: true, -}; - -const MOCK_ACTIONS = { - // CurationsSettingsLogic - loadCurationsSettings: jest.fn(), - onSkipLoadingCurationsSettings: jest.fn(), - // LogRetentionLogic - fetchLogRetention: jest.fn(), -}; - describe('CurationsRouter', () => { - beforeEach(() => { - jest.clearAllMocks(); - setMockActions(MOCK_ACTIONS); - }); - it('renders', () => { const wrapper = shallow(); expect(wrapper.find(Switch)).toHaveLength(1); expect(wrapper.find(Route)).toHaveLength(4); }); - - it('loads log retention settings', () => { - setMockValues(MOCK_VALUES); - shallow(); - - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); - }); - - describe('when the user has no platinum license', () => { - beforeEach(() => { - setMockValues({ - ...MOCK_VALUES, - hasPlatinumLicense: false, - }); - }); - - it('it does not fetch log retention', () => { - shallow(); - expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); - }); - }); - - describe('loading curation settings based on log retention', () => { - it('loads curation settings when log retention is enabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: true, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('skips loading curation settings when log retention is disabled', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: { - [LogRetentionOptions.Analytics]: { - enabled: false, - }, - }, - }); - - shallow(); - - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); - }); - - it('takes no action if log retention has not yet been loaded', () => { - setMockValues({ - ...MOCK_VALUES, - logRetention: null, - }); - - shallow(); - - expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); - expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); - }); - }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx index a3b000ea5054a..693e5406b714b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/curations_router.tsx @@ -5,53 +5,20 @@ * 2.0. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { useValues, useActions } from 'kea'; - -import { LicensingLogic } from '../../../shared/licensing'; import { ENGINE_CURATIONS_PATH, ENGINE_CURATIONS_NEW_PATH, ENGINE_CURATION_PATH, ENGINE_CURATION_SUGGESTION_PATH, } from '../../routes'; -import { LogRetentionLogic, LogRetentionOptions } from '../log_retention'; import { Curation } from './curation'; import { Curations, CurationCreation, CurationSuggestion } from './views'; -import { CurationsSettingsLogic } from './views/curations_settings'; export const CurationsRouter: React.FC = () => { - // We need to loadCurationsSettings here so they are available across all views - - const { hasPlatinumLicense } = useValues(LicensingLogic); - - const { loadCurationsSettings, onSkipLoadingCurationsSettings } = - useActions(CurationsSettingsLogic); - - const { logRetention } = useValues(LogRetentionLogic); - const { fetchLogRetention } = useActions(LogRetentionLogic); - - const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; - - useEffect(() => { - if (hasPlatinumLicense) { - fetchLogRetention(); - } - }, [hasPlatinumLicense]); - - useEffect(() => { - if (logRetention) { - if (!analyticsDisabled) { - loadCurationsSettings(); - } else { - onSkipLoadingCurationsSettings(); - } - } - }, [logRetention]); - return ( diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx index 2207555772b5b..b0f4f03789af2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations.tsx @@ -24,17 +24,15 @@ import { getCurationsBreadcrumbs } from '../utils'; import { CurationsHistory } from './curations_history/curations_history'; import { CurationsOverview } from './curations_overview'; -import { CurationsSettings, CurationsSettingsLogic } from './curations_settings'; +import { CurationsSettings } from './curations_settings'; export const Curations: React.FC = () => { - const { dataLoading: curationsDataLoading, meta, selectedPageTab } = useValues(CurationsLogic); + const { dataLoading, meta, selectedPageTab } = useValues(CurationsLogic); const { loadCurations, onSelectPageTab } = useActions(CurationsLogic); const { engine: { search_relevance_suggestions_active: searchRelevanceSuggestionsActive }, } = useValues(EngineLogic); - const { dataLoading: curationsSettingsDataLoading } = useValues(CurationsSettingsLogic); - const suggestionsEnabled = searchRelevanceSuggestionsActive; const OVERVIEW_TAB = { @@ -82,8 +80,6 @@ export const Curations: React.FC = () => { loadCurations(); }, [meta.page.current]); - const isLoading = curationsSettingsDataLoading || curationsDataLoading; - return ( { {CREATE_NEW_CURATION_TITLE} , ], - tabs: isLoading ? undefined : pageTabs, + tabs: dataLoading ? undefined : pageTabs, }} - isLoading={isLoading} + isLoading={dataLoading} > {selectedPageTab === 'overview' && } {selectedPageTab === 'history' && } diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx index 3b01d1e41c271..4b4e11c31d4b8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.test.tsx @@ -17,6 +17,8 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { EuiButtonEmpty, EuiCallOut, EuiSwitch } from '@elastic/eui'; +import { mountWithIntl } from '@kbn/test/jest'; + import { Loading } from '../../../../../shared/loading'; import { EuiButtonTo } from '../../../../../shared/react_router_helpers'; import { DataPanel } from '../../../data_panel'; @@ -44,6 +46,8 @@ const MOCK_VALUES = { const MOCK_ACTIONS = { // CurationsSettingsLogic + loadCurationsSettings: jest.fn(), + onSkipLoadingCurationsSettings: jest.fn(), toggleCurationsEnabled: jest.fn(), toggleCurationsMode: jest.fn(), // LogRetentionLogic @@ -56,6 +60,14 @@ describe('CurationsSettings', () => { setMockActions(MOCK_ACTIONS); }); + it('loads curations and log retention settings on load', () => { + setMockValues(MOCK_VALUES); + mountWithIntl(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalled(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalled(); + }); + it('contains a switch to toggle curations settings', () => { let wrapper: ShallowWrapper; @@ -154,6 +166,50 @@ describe('CurationsSettings', () => { expect(wrapper.is(Loading)).toBe(true); }); + describe('loading curation settings based on log retention', () => { + it('loads curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: true, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('skips loading curation settings when log retention is enabled', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: { + [LogRetentionOptions.Analytics]: { + enabled: false, + }, + }, + }); + + shallow(); + + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(1); + }); + + it('takes no action if log retention has not yet been loaded', () => { + setMockValues({ + ...MOCK_VALUES, + logRetention: null, + }); + + shallow(); + + expect(MOCK_ACTIONS.loadCurationsSettings).toHaveBeenCalledTimes(0); + expect(MOCK_ACTIONS.onSkipLoadingCurationsSettings).toHaveBeenCalledTimes(0); + }); + }); + describe('when the user has no platinum license', () => { beforeEach(() => { setMockValues({ @@ -162,6 +218,11 @@ describe('CurationsSettings', () => { }); }); + it('it does not fetch log retention', () => { + shallow(); + expect(MOCK_ACTIONS.fetchLogRetention).toHaveBeenCalledTimes(0); + }); + it('shows a CTA to upgrade your license when the user when the user', () => { const wrapper = shallow(); expect(wrapper.is(DataPanel)).toBe(true); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx index a5d4a33d8b870..de669298b11d9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/curations/views/curations_settings/curations_settings.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useEffect } from 'react'; import { useActions, useValues } from 'kea'; @@ -43,12 +43,34 @@ export const CurationsSettings: React.FC = () => { curationsSettings: { enabled, mode }, dataLoading, } = useValues(CurationsSettingsLogic); - const { toggleCurationsEnabled, toggleCurationsMode } = useActions(CurationsSettingsLogic); + const { + loadCurationsSettings, + onSkipLoadingCurationsSettings, + toggleCurationsEnabled, + toggleCurationsMode, + } = useActions(CurationsSettingsLogic); const { isLogRetentionUpdating, logRetention } = useValues(LogRetentionLogic); + const { fetchLogRetention } = useActions(LogRetentionLogic); const analyticsDisabled = !logRetention?.[LogRetentionOptions.Analytics].enabled; + useEffect(() => { + if (hasPlatinumLicense) { + fetchLogRetention(); + } + }, [hasPlatinumLicense]); + + useEffect(() => { + if (logRetention) { + if (!analyticsDisabled) { + loadCurationsSettings(); + } else { + onSkipLoadingCurationsSettings(); + } + } + }, [logRetention]); + if (!hasPlatinumLicense) return ( Date: Wed, 27 Oct 2021 14:22:45 -0600 Subject: [PATCH 08/22] [Security Solutions] Adds bsearch service to FTR e2e tests to reduce flake, boilerplate, and technique choices (#116211) ## Summary Fixes flake tests of: https://github.com/elastic/kibana/issues/115918 https://github.com/elastic/kibana/issues/103273 https://github.com/elastic/kibana/issues/108640 https://github.com/elastic/kibana/issues/109447 https://github.com/elastic/kibana/issues/100630 https://github.com/elastic/kibana/issues/94535 https://github.com/elastic/kibana/issues/104260 Security solution has been using `bsearch` and has encountered flake in various forms. Different developers have been fixing the flake in a few odd ways (myself included) which aren't 100%. This PR introduces a once-in-for-all REST API retry service called `bsearch` which will query `bsearch` and if `bsearch` is not completed because of async occurring due to slower CI runtimes it will continuously call into the `bsearch` with the correct API to ensure it gets a complete response before returning. ## Usage Anyone can use this service like so: ```ts const bsearch = getService('bsearch'); const response = await bsearch.send({ supertest, options: { defaultIndex: ['large_volume_dns_data'], } strategy: 'securitySolutionSearchStrategy', }); ``` If you're using a custom auth then you can set that beforehand like so: ```ts const bsearch = getService('bsearch'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const supertest supertestWithoutAuth.auth(username, password); const response = await bsearch.send({ supertest, options: { defaultIndex: ['large_volume_dns_data'], } strategy: 'securitySolutionSearchStrategy', }); ``` ## Misconceptions in the tests leading to flake * Can you just call the bsearch REST API and it will always return data first time? Not always true, as when CI slows down or data increases `bsearch` will give you back an async reference and then your test will blow up. * Can we wrap the REST API in `retry` to fix the flake? Not always but mostly true, as when CI slows down or data increases `bsearch` could return the async version continuously which could then fail your test. It's also tedious to tell everyone in code reviews to wrap everything in `retry` instead of just fixing it with a service as well as inform new people why we are constantly wrapping these tests in `retry`. * Can we manually parse the `bsearch` if it has `async` for each test? This is true but is error prone and I did this for one test and it's ugly and I had issues as I have to wrap 2 things in `retry` and test several conditions. Also it's harder for people to read the tests rather than just reading there is a service call. Also people in code reviews missed where I had bugs with it. Also lots of boiler plate. * Can we just increase the timeout with `wait_for_completion_timeout` and the tests will pass for sure then? Not true today but maybe true later, as this hasn't been added as plumbing yet. See this [open ticket](https://github.com/elastic/kibana/issues/107241). Even if it is and we increase the timeout to a very large number bsearch might return with an `async` or you might want to test the `async` path. Either way, if/when we add the ability we can increase it within 1 spot which is this service for everyone rather than going to each individual test to add it. If/when it's added if people don't use the bsearch service we can remove it later if we find this is deterministic enough and no one wants to test bsearch features with their strategies down the road. ## Manual test of bsearch service If you want to manually watch the bsearch operate as if the CI system is running slow or to cause an `async` manually you manually modify this setting here: https://github.com/elastic/kibana/blob/master/src/plugins/data/server/search/strategies/ese_search/request_utils.ts#L61 To be of a lower number such as `1ms` and then you will see it enter the `async` code within `bsearch` consistently ## Reference PRs We cannot set the wait_for_complete just yet https://github.com/elastic/kibana/issues/107241 so we decided this was the best way to reduce flake for testing for now. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- test/common/services/bsearch.ts | 122 ++++++++ test/common/services/index.ts | 2 + .../apis/security_solution/authentications.ts | 43 +-- .../apis/security_solution/events.ts | 33 ++- .../apis/security_solution/host_details.ts | 29 +- .../apis/security_solution/hosts.ts | 135 +++++---- .../apis/security_solution/kpi_hosts.ts | 265 +++++++++--------- .../apis/security_solution/kpi_network.ts | 176 ++++++------ .../security_solution/matrix_dns_histogram.ts | 95 ++----- .../apis/security_solution/network_details.ts | 61 ++-- .../apis/security_solution/network_dns.ts | 36 +-- .../security_solution/network_top_n_flow.ts | 63 +++-- .../apis/security_solution/overview_host.ts | 29 +- .../security_solution/overview_network.ts | 77 ++--- .../security_solution/timeline_details.ts | 67 +++-- .../apis/security_solution/tls.ts | 72 ++--- .../security_solution/uncommon_processes.ts | 142 +++++----- .../apis/security_solution/users.ts | 80 +++--- .../security_and_spaces/tests/basic/events.ts | 6 + .../security_and_spaces/tests/trial/events.ts | 4 + 20 files changed, 819 insertions(+), 718 deletions(-) create mode 100644 test/common/services/bsearch.ts diff --git a/test/common/services/bsearch.ts b/test/common/services/bsearch.ts new file mode 100644 index 0000000000000..d9fe89d9e4b9c --- /dev/null +++ b/test/common/services/bsearch.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import request from 'superagent'; +import type SuperTest from 'supertest'; +import { IEsSearchResponse } from 'src/plugins/data/common'; +import { FtrProviderContext } from '../ftr_provider_context'; +import { RetryService } from './retry/retry'; + +/** + * Function copied from here: + * test/api_integration/apis/search/bsearch.ts without the compress + * + * Splits the JSON lines from bsearch + */ +const parseBfetchResponse = (resp: request.Response): Array> => { + return resp.text + .trim() + .split('\n') + .map((item) => JSON.parse(item)); +}; + +/** + * Function copied from here: + * x-pack/test/rule_registry/common/lib/authentication/spaces.ts + * @param spaceId The space id we want to utilize + */ +const getSpaceUrlPrefix = (spaceId?: string): string => { + return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``; +}; + +/** + * Options for the send method + */ +interface SendOptions { + supertest: SuperTest.SuperTest; + options: object; + strategy: string; + space?: string; +} + +/** + * Bsearch factory which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search + * response or a sync response. + * + * @example + * const supertest = getService('supertest'); + * const bsearch = getService('bsearch'); + * const response = await bsearch.send({ + * supertest, + * options: { + * defaultIndex: ['large_volume_dns_data'], + * } + * strategy: 'securitySolutionSearchStrategy', + * }); + * expect(response).eql({ ... your value ... }); + */ +export const BSearchFactory = (retry: RetryService) => ({ + /** Send method to send in your supertest, url, options, and strategy name */ + send: async ({ + supertest, + options, + strategy, + space, + }: SendOptions): Promise => { + const spaceUrl = getSpaceUrlPrefix(space); + const { body } = await retry.try(async () => { + return supertest + .post(`${spaceUrl}/internal/search/${strategy}`) + .set('kbn-xsrf', 'true') + .send(options) + .expect(200); + }); + + if (body.isRunning) { + const result = await retry.try(async () => { + const resp = await supertest + .post(`${spaceUrl}/internal/bsearch`) + .set('kbn-xsrf', 'true') + .send({ + batch: [ + { + request: { + id: body.id, + ...options, + }, + options: { + strategy, + }, + }, + ], + }) + .expect(200); + const [parsedResponse] = parseBfetchResponse(resp); + expect(parsedResponse.result.isRunning).equal(false); + return parsedResponse.result; + }); + return result; + } else { + return body; + } + }, +}); + +/** + * Bsearch provider which will return a new bsearch capable service that can reduce flake + * on the CI systems when they are under pressure and bsearch returns an async search response + * or a sync response. + */ +export function BSearchProvider({ + getService, +}: FtrProviderContext): ReturnType { + const retry = getService('retry'); + return BSearchFactory(retry); +} diff --git a/test/common/services/index.ts b/test/common/services/index.ts index c04bd778468a9..91d17ce1bb3e8 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -16,6 +16,7 @@ import { SecurityServiceProvider } from './security'; import { EsDeleteAllIndicesProvider } from './es_delete_all_indices'; import { SavedObjectInfoService } from './saved_object_info'; import { IndexPatternsService } from './index_patterns'; +import { BSearchProvider } from './bsearch'; export const services = { deployment: DeploymentService, @@ -28,4 +29,5 @@ export const services = { esDeleteAllIndices: EsDeleteAllIndicesProvider, savedObjectInfo: SavedObjectInfoService, indexPatterns: IndexPatternsService, + bsearch: BSearchProvider, }; diff --git a/x-pack/test/api_integration/apis/security_solution/authentications.ts b/x-pack/test/api_integration/apis/security_solution/authentications.ts index 4ea8b8ab82e16..8254ce034d2a5 100644 --- a/x-pack/test/api_integration/apis/security_solution/authentications.ts +++ b/x-pack/test/api_integration/apis/security_solution/authentications.ts @@ -6,7 +6,10 @@ */ import expect from '@kbn/expect'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostAuthenticationsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -22,16 +25,19 @@ const EDGE_LENGTH = 1; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('authentications', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Authentication data', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -47,9 +53,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); @@ -57,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in Authentications query', async () => { - const { body: authentications } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const authentications = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.authentications, timerange: { interval: '12h', @@ -76,16 +81,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(authentications.edges.length).to.be(EDGE_LENGTH); expect(authentications.totalCount).to.be(TOTAL_COUNT); - expect(authentications.edges[0]!.node.lastSuccess!.source!.ip).to.eql([ + expect(authentications.edges[0].node.lastSuccess?.source?.ip).to.eql([ LAST_SUCCESS_SOURCE_IP, ]); - expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); + expect(authentications.edges[0].node.lastSuccess?.host?.name).to.eql([HOST_NAME]); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/events.ts b/x-pack/test/api_integration/apis/security_solution/events.ts index f6a668679b11d..fef37e9939fcb 100644 --- a/x-pack/test/api_integration/apis/security_solution/events.ts +++ b/x-pack/test/api_integration/apis/security_solution/events.ts @@ -11,13 +11,13 @@ import { JsonObject } from '@kbn/utility-types'; import { Direction, TimelineEventsQueries, + TimelineEventsAllStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; import { getDocValueFields, getFieldsToRequest, getFilterValue } from './utils'; const TO = '3000-01-01T00:00:00.000Z'; const FROM = '2000-01-01T00:00:00.000Z'; -const TEST_URL = '/internal/search/timelineSearchStrategy/'; // typical values that have to change after an update from "scripts/es_archiver" const DATA_COUNT = 7; const HOST_NAME = 'suricata-sensor-amsterdam'; @@ -30,6 +30,8 @@ const LIMITED_PAGE_SIZE = 2; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + const getPostBody = (): JsonObject => ({ defaultIndex: ['auditbeat-*'], docValueFields: getDocValueFields(), @@ -66,14 +68,14 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns Timeline data', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send(getPostBody()) - .expect(200); + const timeline = await bsearch.send({ + supertest, + options: { + ...getPostBody(), + }, + strategy: 'timelineSearchStrategy', + }); - const timeline = resp.body; expect(timeline.edges.length).to.be(EDGE_LENGTH); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); @@ -82,20 +84,17 @@ export default function ({ getService }: FtrProviderContext) { }); it('returns paginated Timeline query', async () => { - const resp = await supertest - .post(TEST_URL) - .set('kbn-xsrf', 'true') - .set('Content-Type', 'application/json') - .send({ + const timeline = await bsearch.send({ + supertest, + options: { ...getPostBody(), pagination: { activePage: 0, querySize: LIMITED_PAGE_SIZE, }, - }) - .expect(200); - - const timeline = resp.body; + }, + strategy: 'timelineSearchStrategy', + }); expect(timeline.edges.length).to.be(LIMITED_PAGE_SIZE); expect(timeline.edges[0].node.data.length).to.be(DATA_COUNT); expect(timeline.totalCount).to.be(TOTAL_COUNT); diff --git a/x-pack/test/api_integration/apis/security_solution/host_details.ts b/x-pack/test/api_integration/apis/security_solution/host_details.ts index 114f60a21c4e3..d2de0f84a3769 100644 --- a/x-pack/test/api_integration/apis/security_solution/host_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/host_details.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostDetailsStrategyResponse, + HostsQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Host Details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -213,12 +221,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get HostDetails data', async () => { - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { hostDetails } = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.details, timerange: { interval: '12h', @@ -229,9 +234,9 @@ export default function ({ getService }: FtrProviderContext) { docValueFields: [], hostName: 'raspberrypi', inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hostDetails).to.eql(expectedResult.hostDetails); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/hosts.ts b/x-pack/test/api_integration/apis/security_solution/hosts.ts index 4df46002e9a13..bb2969f85a98b 100644 --- a/x-pack/test/api_integration/apis/security_solution/hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/hosts.ts @@ -10,6 +10,9 @@ import { HostsQueries, Direction, HostsFields, + HostsStrategyResponse, + HostDetailsStrategyResponse, + HostFirstLastSeenStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -26,17 +29,19 @@ const CURSOR_ID = '2ab45fc1c41e4c84bbd02202a7e5761f'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); - // Failing: See https://github.com/elastic/kibana/issues/104260 - describe.skip('hosts', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts')); + describe('hosts', () => { + before(async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts')); + + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts') + ); it('Make sure that we get Hosts Table data', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -56,19 +61,18 @@ export default function ({ getService }: FtrProviderContext) { querySize: 1, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); expect(hosts.pageInfo.fakeTotalCount).to.equal(3); }); it('Make sure that pagination is working in Hosts Table query', async () => { - const { body: hosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const hosts = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.hosts, timerange: { interval: '12h', @@ -88,16 +92,32 @@ export default function ({ getService }: FtrProviderContext) { querySize: 2, }, inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(hosts.edges.length).to.be(EDGE_LENGTH); expect(hosts.totalCount).to.be(TOTAL_COUNT); - expect(hosts.edges[0]!.node.host!.os!.name).to.eql([HOST_NAME]); + expect(hosts.edges[0].node.host?.os?.name).to.eql([HOST_NAME]); }); it('Make sure that we get Host details data', async () => { - const expectedHostDetails = { + const { hostDetails } = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.details, + hostName: 'zeek-sensor-san-francisco', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(hostDetails).to.eql({ _id: 'zeek-sensor-san-francisco', host: { architecture: ['x86_64'], @@ -122,91 +142,66 @@ export default function ({ getService }: FtrProviderContext) { provider: ['digitalocean'], region: ['sfo2'], }, - }; - const { - body: { hostDetails }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.details, - hostName: 'zeek-sensor-san-francisco', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(hostDetails).to.eql(expectedHostDetails); + }); }); it('Make sure that we get First Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql('2019-02-19T19:36:23.561Z'); }); it('Make sure that we get Last Seen for a Host without docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*'], docValueFields: [], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql('2019-02-19T20:42:33.561Z'); }); it('Make sure that we get First Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'asc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.firstSeen).to.eql(new Date('2019-02-19T19:36:23.561Z').valueOf()); }); it('Make sure that we get Last Seen for a Host with docValueFields', async () => { - const { body: firstLastSeenHost } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const firstLastSeenHost = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.firstOrLastSeen, defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [{ field: '@timestamp', format: 'epoch_millis' }], hostName: 'zeek-sensor-san-francisco', order: 'desc', - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(firstLastSeenHost.lastSeen).to.eql(new Date('2019-02-19T20:42:33.561Z').valueOf()); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts index 632f738d85f36..f38cf406a9dbe 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts @@ -6,18 +6,27 @@ */ import expect from '@kbn/expect'; -import { HostsKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsKpiAuthenticationsStrategyResponse, + HostsKpiHostsStrategyResponse, + HostsKpiQueries, + HostsKpiUniqueIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Hosts', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -50,88 +59,80 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['filebeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -188,79 +189,69 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiHosts data', async () => { - await retry.try(async () => { - const { body: kpiHosts } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiHosts, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - - expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); - expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + const kpiHosts = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts).to.eql(expectedResult.hosts); }); it('Make sure that we get KpiAuthentications data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiAuthentications, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); - expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); - expect(body.authenticationsFailure!).to.eql(expectedResult.authFailure); - expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure).to.eql(expectedResult.authFailure); + expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram); }); it('Make sure that we get KpiUniqueIps data', async () => { - await retry.try(async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsKpiQueries.kpiUniqueIps, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); - expect(body.uniqueDestinationIpsHistogram!).to.eql( - expectedResult.uniqueDestinationIpsHistogram - ); - expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); - expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); + const body = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram!).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts index 53b099bbe18d3..637b69ff1daaa 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts @@ -7,16 +7,28 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkKpiDnsStrategyResponse, + NetworkKpiNetworkEventsStrategyResponse, + NetworkKpiQueries, + NetworkKpiTlsHandshakesStrategyResponse, + NetworkKpiUniqueFlowsStrategyResponse, + NetworkKpiUniquePrivateIpsStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Kpi Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -66,10 +78,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -79,18 +90,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -100,18 +109,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -121,18 +128,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -142,18 +147,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -163,18 +166,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -184,9 +186,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['filebeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps @@ -202,8 +204,12 @@ export default function ({ getService }: FtrProviderContext) { }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -219,10 +225,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get KpiNetwork uniqueFlows data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniqueFlows, timerange: { interval: '12h', @@ -232,18 +237,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); }); it('Make sure that we get KpiNetwork DNS data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.dns, timerange: { interval: '12h', @@ -253,18 +256,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); }); it('Make sure that we get KpiNetwork networkEvents data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.networkEvents, timerange: { interval: '12h', @@ -274,18 +275,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); }); it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.tlsHandshakes, timerange: { interval: '12h', @@ -295,18 +295,16 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); }); it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { - const { body: kpiNetwork } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const kpiNetwork = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkKpiQueries.uniquePrivateIps, timerange: { interval: '12h', @@ -316,9 +314,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( expectedResult.uniqueDestinationPrivateIps diff --git a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts index 6040ecd1001d9..c7b6bbb84436f 100644 --- a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts +++ b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts @@ -6,54 +6,43 @@ */ import expect from '@kbn/expect'; -import request from 'superagent'; import { MatrixHistogramQuery, MatrixHistogramType, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -/** - * Function copied from here: - * test/api_integration/apis/search/bsearch.ts - * - * Splits the JSON lines from bsearch - */ -export const parseBfetchResponse = (resp: request.Response): Array> => { - return resp.text - .trim() - .split('\n') - .map((item) => JSON.parse(item)); -}; - export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); - const retry = getService('retry'); + const bsearch = getService('bsearch'); describe('Matrix DNS Histogram', () => { describe('Large data set', () => { - before(() => - esArchiver.load( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + before( + async () => + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); - after(() => - esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' - ) + + after( + async () => + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query' + ) ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get dns data without getting bucket errors when querying large volume of data', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['large_volume_dns_data'], docValueFields: [], factoryQueryType: MatrixHistogramQuery, @@ -62,56 +51,14 @@ export default function ({ getService }: FtrProviderContext) { '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', isPtrIncluded: false, timerange: { - interval: '12h', + interval: '12', to: TO, from: FROM, }, - }) - .expect(200); - - if (networkDns.isRunning === true) { - await retry.waitForWithTimeout('bsearch to give us results', 5000, async () => { - const resp = await supertest - .post('/internal/bsearch') - .set('kbn-xsrf', 'true') - .send({ - batch: [ - { - request: { - id: networkDns.id, - defaultIndex: ['large_volume_dns_data'], - docValueFields: [], - factoryQueryType: MatrixHistogramQuery, - histogramType: MatrixHistogramType.dns, - filterQuery: - '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', - isPtrIncluded: false, - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - }, - options: { - strategy: 'securitySolutionSearchStrategy', - }, - }, - ], - }); - const parsedResponse = parseBfetchResponse(resp); - // NOTE: I would like this test to be ".to.equal(6604)" but that is flakey as sometimes the query - // does not give me that exact value. It gives me failures as seen here with notes: https://github.com/elastic/kibana/issues/97365 - // I don't think this is a bug with the query but possibly a consistency view issue with interacting with the archive - // so we instead loosen this test up a bit to avoid flake. - expect(parsedResponse[0].result.rawResponse.aggregations.dns_count.value).to.be.above( - 0 - ); - return true; - }); - } else { - expect(networkDns.isRunning).to.equal(false); - expect(networkDns.rawResponse.aggregations.dns_count.value).to.equal(6604); - } + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkDns.rawResponse.aggregations?.dns_count).to.eql({ value: 6604 }); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index 0397e7550c935..07e526c7c4bf5 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -6,59 +6,70 @@ */ import expect from '@kbn/expect'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkDetailsStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Network details', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.source!.geo!.continent_name).to.be('North America'); - expect(body.networkDetails!.source!.geo!.location!.lat!).to.be(37.751); - expect(body.networkDetails!.host.os!.platform!).to.eql(['raspbian']); + expect(body.networkDetails.source?.geo.continent_name).to.be('North America'); + expect(body.networkDetails.source?.geo.location?.lat!).to.be(37.751); + expect(body.networkDetails.host?.os?.platform).to.eql(['raspbian']); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default') + ); it('Make sure that we get Network details data', async () => { - const { body } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const body = await bsearch.send({ + supertest, + options: { ip: '185.53.91.88', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.details, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); - expect(body.networkDetails!.host.id!).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); - expect(body.networkDetails!.host.name!).to.eql(['zeek-sensor-amsterdam']); - expect(body.networkDetails!.host.os!.platform!).to.eql(['ubuntu']); + expect(body.networkDetails.host?.id).to.eql(['2ce8b1e7d69e4a1d9c6bcddc473da9d9']); + expect(body.networkDetails.host?.name).to.eql(['zeek-sensor-amsterdam']); + expect(body.networkDetails.host?.os?.platform!).to.eql(['ubuntu']); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_dns.ts b/x-pack/test/api_integration/apis/security_solution/network_dns.ts index 80660860a164b..474c6261babf0 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_dns.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_dns.ts @@ -11,6 +11,7 @@ import { NetworkDnsEdges, Direction, NetworkDnsFields, + NetworkDnsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -18,20 +19,24 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network DNS', () => { describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/dns') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/dns') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; it('Make sure that we get Dns data and sorting by uniqueDomains ascending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], docValueFields: [], factoryQueryType: NetworkQueries.dns, @@ -45,9 +50,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); @@ -58,10 +63,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Dns data and sorting by uniqueDomains descending', async () => { - const { body: networkDns } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkDns = await bsearch.send({ + supertest, + options: { ip: '151.205.0.17', defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.dns, @@ -80,9 +84,9 @@ export default function ({ getService }: FtrProviderContext) { to: TO, from: FROM, }, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkDns.edges.length).to.be(10); expect(networkDns.totalCount).to.be(44); diff --git a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts index af8e543907492..41b083ab90578 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts @@ -12,6 +12,7 @@ import { Direction, FlowTargetSourceDest, NetworkTopTablesFields, + NetworkTopNFlowStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -21,20 +22,24 @@ const EDGE_LENGTH = 10; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Network Top N Flow', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2019-02-09T01:57:24.870Z'; const TO = '2019-02-12T01:57:24.870Z'; it('Make sure that we get Source NetworkTopNFlow data with bytes_in descending sort', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.topNFlow, flowTarget: FlowTargetSourceDest.source, @@ -52,9 +57,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -70,10 +75,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Source NetworkTopNFlow data with bytes_in ascending sort ', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -93,9 +97,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); @@ -111,10 +115,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that we get Destination NetworkTopNFlow data', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -134,9 +137,10 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(154); expect(networkTopNFlow.edges[0].node.destination!.flows).to.be(19); @@ -146,10 +150,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('Make sure that pagination is working in NetworkTopNFlow query', async () => { - const { body: networkTopNFlow } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const networkTopNFlow = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: 'topNFlow', filterQuery: @@ -169,9 +172,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); expect(networkTopNFlow.totalCount).to.be(121); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index 09bd09782d2f2..b2a32ac46c3f3 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -8,16 +8,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + HostsQueries, + HostsOverviewStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Host', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -41,12 +49,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewHost data', async () => { - const { - body: { overviewHost }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewHost } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: HostsQueries.overview, timerange: { @@ -56,9 +61,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewHost).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_network.ts b/x-pack/test/api_integration/apis/security_solution/overview_network.ts index 00adc903d5733..ffee9b851ffc0 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_network.ts @@ -7,16 +7,24 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + NetworkOverviewStrategyResponse, + NetworkQueries, +} from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Overview Network', () => { describe('With filebeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -34,12 +42,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['filebeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -49,16 +54,21 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With packetbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/overview') + ); + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -75,12 +85,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['packetbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -90,17 +97,20 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/overview') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/overview') + ); const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -117,12 +127,9 @@ export default function ({ getService }: FtrProviderContext) { }; it('Make sure that we get OverviewNetwork data', async () => { - const { - body: { overviewNetwork }, - } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { overviewNetwork } = await bsearch.send({ + supertest, + options: { defaultIndex: ['auditbeat-*'], factoryQueryType: NetworkQueries.overview, timerange: { @@ -132,9 +139,9 @@ export default function ({ getService }: FtrProviderContext) { }, docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(overviewNetwork).to.eql(expectedResult); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts index 3aefd9f8b597a..9f1b5fccd9d1a 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts @@ -7,7 +7,11 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; -import { TimelineEventsQueries } from '../../../../plugins/security_solution/common/search_strategy'; +import { + TimelineEventsQueries, + TimelineEventsDetailsStrategyResponse, + TimelineKpiStrategyResponse, +} from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -668,54 +672,49 @@ const EXPECTED_KPI_COUNTS = { }; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Timeline Details', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default') + ); it('Make sure that we get Event Details data', async () => { - await retry.try(async () => { - const { - body: { data: detailsData }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: TimelineEventsQueries.details, - docValueFields: [], - indexName: INDEX_NAME, - inspect: false, - eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); + const { data: detailsData } = await bsearch.send({ + supertest, + options: { + factoryQueryType: TimelineEventsQueries.details, + docValueFields: [], + indexName: INDEX_NAME, + inspect: false, + eventId: ID, + }, + strategy: 'timelineSearchStrategy', }); + expect(sortBy(detailsData, 'field')).to.eql(sortBy(EXPECTED_DATA, 'field')); }); it('Make sure that we get kpi data', async () => { - await retry.try(async () => { - const { - body: { destinationIpCount, hostCount, processCount, sourceIpCount, userCount }, - } = await supertest - .post('/internal/search/timelineSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const { destinationIpCount, hostCount, processCount, sourceIpCount, userCount } = + await bsearch.send({ + supertest, + options: { factoryQueryType: TimelineEventsQueries.kpi, docValueFields: [], indexName: INDEX_NAME, inspect: false, eventId: ID, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( - EXPECTED_KPI_COUNTS - ); - }); + }, + strategy: 'timelineSearchStrategy', + }); + expect({ destinationIpCount, hostCount, processCount, sourceIpCount, userCount }).to.eql( + EXPECTED_KPI_COUNTS + ); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/tls.ts b/x-pack/test/api_integration/apis/security_solution/tls.ts index 9fa251ded4e6b..c872844cb3d46 100644 --- a/x-pack/test/api_integration/apis/security_solution/tls.ts +++ b/x-pack/test/api_integration/apis/security_solution/tls.ts @@ -11,6 +11,7 @@ import { Direction, NetworkTlsFields, FlowTarget, + NetworkTlsStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -83,17 +84,21 @@ const expectedOverviewSourceResult = { export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('Tls Test with Packetbeat', () => { describe('Tls Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -112,19 +117,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -143,9 +147,9 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); expect(tls.edges.length).to.be(1); expect(tls.totalCount).to.be(1); expect(tls.edges[0].node).to.eql(expectedResult); @@ -153,14 +157,17 @@ export default function ({ getService }: FtrProviderContext) { }); describe('Tls Overview Test', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/tls') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/tls') + ); it('Ensure data is returned for FlowTarget.Source', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -179,18 +186,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewSourceResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewSourceResult.edges[0]); }); it('Ensure data is returned for FlowTarget.Destination', async () => { - const { body: tls } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const tls = await bsearch.send({ + supertest, + options: { factoryQueryType: NetworkQueries.tls, timerange: { interval: '12h', @@ -209,9 +216,10 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['packetbeat-*'], docValueFields: [], inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(tls.pageInfo).to.eql(expectedOverviewDestinationResult.pageInfo); expect(tls.edges[0]).to.eql(expectedOverviewDestinationResult.edges[0]); }); diff --git a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts index d39cc0afb6461..a6749b27030e1 100644 --- a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts +++ b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts @@ -13,10 +13,6 @@ import { } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; -interface UncommonProcessesResponse { - body: HostsUncommonProcessesStrategyResponse; -} - const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; @@ -24,24 +20,51 @@ const TO = '3000-01-01T00:00:00.000Z'; const TOTAL_COUNT = 3; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); describe('uncommon_processes', () => { - before(() => - esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + before( + async () => + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); - after(() => - esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') + after( + async () => + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/uncommon_processes') ); it('should return an edge of length 1 when given a pagination of length 1', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ + const response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', + }); + expect(response.edges.length).to.be(1); + }); + + describe('when given a pagination of length 2', () => { + it('should return an edge of length 2 ', async () => { + const response = await bsearch.send({ + supertest, + options: { factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { @@ -53,84 +76,51 @@ export default function ({ getService }: FtrProviderContext) { activePage: 0, cursorStart: 0, fakePossibleCount: 3, - querySize: 1, + querySize: 2, }, defaultIndex: ['auditbeat-uncommon-processes'], docValueFields: [], inspect: false, - }) - .expect(200); - expect(response!.body.edges.length).to.be(1); - }); - }); - - describe('when given a pagination of length 2', () => { - it('should return an edge of length 2 ', async () => { - await retry.try(async () => { - const response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 2, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(response!.body.edges.length).to.be(2); + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(response.edges.length).to.be(2); }); }); describe('when given a pagination of length 1', () => { - let response: null | UncommonProcessesResponse = null; + let response: HostsUncommonProcessesStrategyResponse | null = null; before(async () => { - await retry.try(async () => { - response = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: HostsQueries.uncommonProcesses, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 1, - }, - defaultIndex: ['auditbeat-uncommon-processes'], - docValueFields: [], - inspect: false, - wait_for_completion_timeout: '10s', - }) - .expect(200); + response = await bsearch.send({ + supertest, + options: { + factoryQueryType: HostsQueries.uncommonProcesses, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-uncommon-processes'], + docValueFields: [], + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); }); it('should return an edge of length 1 ', () => { - expect(response!.body.edges.length).to.be(1); + expect(response?.edges.length).to.be(1); }); it('should return a total count of elements', () => { - expect(response!.body.totalCount).to.be(TOTAL_COUNT); + expect(response?.totalCount).to.be(TOTAL_COUNT); }); it('should return a single data set with pagination of 1', () => { @@ -152,7 +142,7 @@ export default function ({ getService }: FtrProviderContext) { }, ], }; - expect(response!.body.edges[0].node).to.eql(expected); + expect(response?.edges[0].node).to.eql(expected); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/users.ts b/x-pack/test/api_integration/apis/security_solution/users.ts index 84335cc2695ce..d592c99bf006f 100644 --- a/x-pack/test/api_integration/apis/security_solution/users.ts +++ b/x-pack/test/api_integration/apis/security_solution/users.ts @@ -11,6 +11,7 @@ import { Direction, NetworkUsersFields, FlowTarget, + NetworkUsersStrategyResponse, } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -20,53 +21,52 @@ const TO = '3000-01-01T00:00:00.000Z'; const IP = '0.0.0.0'; export default function ({ getService }: FtrProviderContext) { - const retry = getService('retry'); const esArchiver = getService('esArchiver'); const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + describe('Users', () => { describe('With auditbeat', () => { - before(() => esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users')); - after(() => esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users')); + before( + async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/users') + ); + after( + async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/users') + ); it('Ensure data is returned from auditbeat', async () => { - await retry.try(async () => { - const { body: users } = await supertest - .post('/internal/search/securitySolutionSearchStrategy/') - .set('kbn-xsrf', 'true') - .send({ - factoryQueryType: NetworkQueries.users, - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-users'], - docValueFields: [], - ip: IP, - flowTarget: FlowTarget.destination, - sort: { field: NetworkUsersFields.name, direction: Direction.asc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - inspect: false, - /* We need a very long timeout to avoid returning just partial data. - ** https://github.com/elastic/kibana/blob/master/x-pack/test/api_integration/apis/search/search.ts#L18 - */ - wait_for_completion_timeout: '10s', - }) - .expect(200); - expect(users.edges.length).to.be(1); - expect(users.totalCount).to.be(1); - expect(users.edges[0].node.user!.id).to.eql(['0']); - expect(users.edges[0].node.user!.name).to.be('root'); - expect(users.edges[0].node.user!.groupId).to.eql(['0']); - expect(users.edges[0].node.user!.groupName).to.eql(['root']); - expect(users.edges[0].node.user!.count).to.be(1); + const users = await bsearch.send({ + supertest, + options: { + factoryQueryType: NetworkQueries.users, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-users'], + docValueFields: [], + ip: IP, + flowTarget: FlowTarget.destination, + sort: { field: NetworkUsersFields.name, direction: Direction.asc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, + }, + inspect: false, + }, + strategy: 'securitySolutionSearchStrategy', }); + expect(users.edges.length).to.be(1); + expect(users.totalCount).to.be(1); + expect(users.edges[0].node.user?.id).to.eql(['0']); + expect(users.edges[0].node.user?.name).to.be('root'); + expect(users.edges[0].node.user?.groupId).to.eql(['0']); + expect(users.edges[0].node.user?.groupName).to.eql(['root']); + expect(users.edges[0].node.user?.count).to.be(1); }); }); }); diff --git a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts index e44f29c41640f..1e9cfe3eb841b 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/basic/events.ts @@ -135,6 +135,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -164,6 +166,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -183,6 +187,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) diff --git a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts index 0a73009196baf..91ad87737805f 100644 --- a/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts +++ b/x-pack/test/timeline/security_and_spaces/tests/trial/events.ts @@ -117,6 +117,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should be able to view alerts from "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); const resp = await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) @@ -145,6 +147,8 @@ export default ({ getService }: FtrProviderContext) => { it(`${username} should NOT be able to access "${featureIds.join(',')}" ${ space != null ? `in space ${space}` : 'when no space specified' }`, async () => { + // This will be flake until it uses the bsearch service, but these tests aren't operational. Once you do make this operational + // use const bsearch = getService('bsearch'); await supertestWithoutAuth .post(`${getSpaceUrlPrefix(space)}${TEST_URL}`) .auth(username, password) From 9978803893398b67e95d6b8ad64f592beed99131 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 21:24:35 +0100 Subject: [PATCH 09/22] chore(NA): adds backport config for 8.1.0 bump (#116431) --- .backportrc.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.backportrc.json b/.backportrc.json index 89eefb2e3c442..24e08d4768d9a 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -2,6 +2,7 @@ "upstream": "elastic/kibana", "targetBranchChoices": [ { "name": "master", "checked": true }, + "8.0", "7.16", "7.15", "7.14", @@ -32,7 +33,7 @@ ], "targetPRLabels": ["backport"], "branchLabelMapping": { - "^v8.0.0$": "master", + "^v8.1.0$": "master", "^v(\\d+).(\\d+).\\d+$": "$1.$2" }, "autoMerge": true, From edc8f9583bdf0e8d7e7cee1823af1c667ebc2b1e Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Wed, 27 Oct 2021 21:24:48 +0100 Subject: [PATCH 10/22] chore(NA): bump version to 8.1.0 (#116429) * chore(NA): bump version to 8.1.0 * chore(NA): update non supported plugins * chore(NA): update non supported plugins * chore(NA): skip failing tests * chore(NA): update jest snapshots Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- .../integration_tests/corrupt_outdated_docs.test.ts | 2 +- .../migrationsv2/integration_tests/outdated_docs.test.ts | 2 +- x-pack/package.json | 2 +- x-pack/plugins/file_upload/kibana.json | 2 +- x-pack/plugins/global_search_bar/kibana.json | 2 +- x-pack/plugins/graph/kibana.json | 4 ++-- x-pack/plugins/grokdebugger/kibana.json | 2 +- x-pack/plugins/index_management/common/constants/plugin.ts | 2 +- x-pack/plugins/ingest_pipelines/kibana.json | 2 +- .../analytics_list/__mocks__/analytics_list_item.json | 2 +- x-pack/plugins/painless_lab/kibana.json | 2 +- x-pack/plugins/remote_clusters/common/constants.ts | 2 +- x-pack/plugins/stack_alerts/kibana.json | 2 +- x-pack/plugins/task_manager/kibana.json | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 3c42c077ae5b0..0e5c042e4236d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dashboarding" ], "private": true, - "version": "8.0.0", + "version": "8.1.0", "branch": "master", "types": "./kibana.d.ts", "tsdocMetadata": "./build/tsdoc-metadata.json", diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts index 348cbe88cd8a7..ebc632f059ced 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/corrupt_outdated_docs.test.ts @@ -40,7 +40,7 @@ describe('migration v2 with corrupt saved object documents', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('collects corrupt saved object documents across batches', async () => { + it.skip('collects corrupt saved object documents across batches', async () => { const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), settings: { diff --git a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts index 506f42cb2e402..9fd8f3884c4ae 100644 --- a/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts +++ b/src/core/server/saved_objects/migrationsv2/integration_tests/outdated_docs.test.ts @@ -41,7 +41,7 @@ describe('migration v2', () => { await new Promise((resolve) => setTimeout(resolve, 10000)); }); - it('migrates the documents to the highest version', async () => { + it.skip('migrates the documents to the highest version', async () => { const migratedIndex = `.kibana_${pkg.version}_001`; const { startES } = kbnTestServer.createTestServers({ adjustTimeout: (t: number) => jest.setTimeout(t), diff --git a/x-pack/package.json b/x-pack/package.json index 805d8555bf453..8fb7a3483e5ef 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -1,6 +1,6 @@ { "name": "x-pack", - "version": "8.0.0", + "version": "8.1.0", "author": "Elastic", "private": true, "license": "Elastic-License", diff --git a/x-pack/plugins/file_upload/kibana.json b/x-pack/plugins/file_upload/kibana.json index e69c5e34bc09b..f70bc6377972a 100644 --- a/x-pack/plugins/file_upload/kibana.json +++ b/x-pack/plugins/file_upload/kibana.json @@ -1,6 +1,6 @@ { "id": "fileUpload", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, diff --git a/x-pack/plugins/global_search_bar/kibana.json b/x-pack/plugins/global_search_bar/kibana.json index 5c0a9999b8e3a..94b8aba2dd7f9 100644 --- a/x-pack/plugins/global_search_bar/kibana.json +++ b/x-pack/plugins/global_search_bar/kibana.json @@ -4,7 +4,7 @@ "name": "Kibana Core", "githubTeam": "kibana-core" }, - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": false, "ui": true, diff --git a/x-pack/plugins/graph/kibana.json b/x-pack/plugins/graph/kibana.json index 410a5e2c160d6..03729c706df25 100644 --- a/x-pack/plugins/graph/kibana.json +++ b/x-pack/plugins/graph/kibana.json @@ -1,6 +1,6 @@ { "id": "graph", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "server": true, "ui": true, @@ -29,4 +29,4 @@ "name": "Data Discovery", "githubTeam": "kibana-data-discovery" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/grokdebugger/kibana.json b/x-pack/plugins/grokdebugger/kibana.json index 692aa16329d54..f8cb75f7d10b6 100644 --- a/x-pack/plugins/grokdebugger/kibana.json +++ b/x-pack/plugins/grokdebugger/kibana.json @@ -1,6 +1,6 @@ { "id": "grokdebugger", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/index_management/common/constants/plugin.ts b/x-pack/plugins/index_management/common/constants/plugin.ts index ad57398000426..060d42ca26b02 100644 --- a/x-pack/plugins/index_management/common/constants/plugin.ts +++ b/x-pack/plugins/index_management/common/constants/plugin.ts @@ -22,4 +22,4 @@ export const PLUGIN = { // "PluginInitializerContext.env.packageInfo.version". In some cases it is not possible // to dynamically inject that version without a huge refactor on the code base. // We will then keep this single constant to declare on which major branch we are. -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json index 800d92b5c9748..889559826f1f1 100644 --- a/x-pack/plugins/ingest_pipelines/kibana.json +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -1,6 +1,6 @@ { "id": "ingestPipelines", - "version": "8.0.0", + "version": "8.1.0", "server": true, "ui": true, "owner": { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json index 20343755f7f0f..1b7d353d9f303 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/__mocks__/analytics_list_item.json @@ -22,7 +22,7 @@ }, "model_memory_limit": "50mb", "create_time": 1568974998023, - "version": "8.0.0" + "version": "8.1.0" }, "id": "fq_outlier_1222", "checkpointing": {}, diff --git a/x-pack/plugins/painless_lab/kibana.json b/x-pack/plugins/painless_lab/kibana.json index 7c71d4bdb4b76..1f59bf30bb761 100644 --- a/x-pack/plugins/painless_lab/kibana.json +++ b/x-pack/plugins/painless_lab/kibana.json @@ -1,6 +1,6 @@ { "id": "painlessLab", - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "owner": { "name": "Stack Management", diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 5a36924b26433..072d6d437b8b9 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -20,7 +20,7 @@ export const PLUGIN = { }, }; -export const MAJOR_VERSION = '8.0.0'; +export const MAJOR_VERSION = '8.1.0'; export const API_BASE_PATH = '/api/remote_clusters'; diff --git a/x-pack/plugins/stack_alerts/kibana.json b/x-pack/plugins/stack_alerts/kibana.json index 1b4271328c2f9..693bcf2f8dbca 100644 --- a/x-pack/plugins/stack_alerts/kibana.json +++ b/x-pack/plugins/stack_alerts/kibana.json @@ -5,7 +5,7 @@ "githubTeam": "kibana-alerting-services" }, "server": true, - "version": "8.0.0", + "version": "8.1.0", "kibanaVersion": "kibana", "requiredPlugins": [ "alerting", diff --git a/x-pack/plugins/task_manager/kibana.json b/x-pack/plugins/task_manager/kibana.json index d0b847ce58d77..36e68ca00af81 100644 --- a/x-pack/plugins/task_manager/kibana.json +++ b/x-pack/plugins/task_manager/kibana.json @@ -1,7 +1,7 @@ { "id": "taskManager", "server": true, - "version": "8.0.0", + "version": "8.1.0", "owner": { "name": "Kibana Alerting", "githubTeam": "kibana-alerting-services" From dc2bea778cd7561794b15a763ad4061b8d996f13 Mon Sep 17 00:00:00 2001 From: Devon Thomson Date: Wed, 27 Oct 2021 16:43:00 -0400 Subject: [PATCH 11/22] Lowered bundle size limits for dashboard and presentationUtil (#116478) --- packages/kbn-optimizer/limits.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 453e5c174452d..99f9c04069b72 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -8,7 +8,7 @@ pageLoadAssetSize: console: 46091 core: 435325 crossClusterReplication: 65408 - dashboard: 186763 + dashboard: 82025 dashboardEnhanced: 65646 devTools: 38637 discover: 99999 @@ -76,7 +76,7 @@ pageLoadAssetSize: watcher: 43598 runtimeFields: 41752 stackAlerts: 29684 - presentationUtil: 94301 + presentationUtil: 84606 osquery: 107090 fileUpload: 25664 dataVisualizer: 27530 From 83288af8bd200e607b31d7c50253786a9d1a9382 Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 27 Oct 2021 21:34:41 +0000 Subject: [PATCH 12/22] skip failing suite (#72874) --- x-pack/test/security_solution_endpoint/apps/endpoint/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 70d60ba5c1b67..d1cfddbca3a9c 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -15,7 +15,8 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + // FAILING: https://github.com/elastic/kibana/issues/72874 + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); From 17df3a265472a2a666d07b07b8a00a31702efc31 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Oct 2021 17:03:37 -0500 Subject: [PATCH 13/22] [ci] bump timeout for cigroups until we figure out the slowdown (#116506) Co-authored-by: spalger --- .buildkite/pipelines/hourly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 3337cfb5dfcdd..b03a46b5b5c66 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -21,7 +21,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 150 + timeout_in_minutes: 250 key: default-cigroup retry: automatic: From 4efabacd4ef59128582089bff1f594658dbf40ed Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Wed, 27 Oct 2021 17:38:53 -0500 Subject: [PATCH 14/22] [Security Solution] fix endpoint being visible after unenrolling agent (#116372) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/fleet/server/services/agents/helpers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/fleet/server/services/agents/helpers.ts b/x-pack/plugins/fleet/server/services/agents/helpers.ts index 609d5ba6c83a0..a4a7803d35e2c 100644 --- a/x-pack/plugins/fleet/server/services/agents/helpers.ts +++ b/x-pack/plugins/fleet/server/services/agents/helpers.ts @@ -42,5 +42,9 @@ export function agentSOAttributesToFleetServerAgentDoc( doc.policy_revision_idx = policyRevison; } + if (!doc.updated_at) { + doc.updated_at = new Date().toISOString(); + } + return doc; } From ede3882e1857df38cbd5595de3916f435614d1b0 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 27 Oct 2021 20:55:23 -0400 Subject: [PATCH 15/22] [Fleet] Fix back button when adding integration from multiple integrations package (#116352) --- .../integrations/sections/epm/screens/detail/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 6e3eba19c52e3..c8c6f49356810 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -271,6 +271,7 @@ export function Detail() { { path: pagePathGetters.integration_details_policies({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ]; @@ -289,6 +290,7 @@ export function Detail() { { path: pagePathGetters.integration_details_overview({ pkgkey, + ...(integration ? { integration } : {}), })[1], }, ], From 9e6e84571f92b4602681ea0f9171f5b8cc0643d8 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 27 Oct 2021 20:38:43 -0500 Subject: [PATCH 16/22] Revert "[Canvas] By-Value Embeddables (#113827)" (#116527) This reverts commit bacd7f91dd8e2904c9b88523ddb46edd1d41fe2e. Co-authored-by: spalger --- .../application/top_nav/editor_menu.tsx | 2 +- .../server/collectors/management/schema.ts | 4 - .../server/collectors/management/types.ts | 1 - src/plugins/presentation_util/common/labs.ts | 17 +- .../solution_toolbar/items/quick_group.scss | 34 +--- src/plugins/telemetry/schema/oss_plugins.json | 6 - .../expression_types/embeddable.ts | 3 +- .../functions/browser/index.ts | 12 +- .../functions/external/embeddable.test.ts | 60 ------- .../functions/external/embeddable.ts | 145 --------------- .../functions/external/index.ts | 19 +- .../functions/external/saved_lens.ts | 16 +- .../functions/external/saved_map.ts | 5 +- .../functions/external/saved_visualization.ts | 3 +- .../canvas/canvas_plugin_src/plugin.ts | 10 -- .../renderers/embeddable/embeddable.tsx | 56 +++--- .../embeddable_input_to_expression.ts | 8 +- .../embeddable.test.ts | 128 ------------- .../input_type_to_expression/embeddable.ts | 13 -- .../input_type_to_expression/lens.test.ts | 5 +- .../input_type_to_expression/lens.ts | 2 +- .../input_type_to_expression/map.test.ts | 12 +- .../input_type_to_expression/map.ts | 7 +- .../visualization.test.ts | 5 +- .../input_type_to_expression/visualization.ts | 4 +- .../canvas/common/lib/embeddable_dataurl.ts | 13 -- .../canvas/i18n/functions/dict/embeddable.ts | 25 --- .../canvas/i18n/functions/function_help.ts | 2 - x-pack/plugins/canvas/kibana.json | 1 - .../embeddable_flyout/flyout.component.tsx | 44 ++--- .../components/embeddable_flyout/flyout.tsx | 21 +-- .../public/components/hooks/workpad/index.tsx | 2 - .../hooks/workpad/use_incoming_embeddable.ts | 86 --------- .../public/components/workpad/workpad.tsx | 4 - .../components/workpad_app/workpad_app.scss | 2 +- .../editor_menu.stories.storyshot | 81 --------- .../__stories__/editor_menu.stories.tsx | 107 ----------- .../editor_menu/editor_menu.component.tsx | 170 ------------------ .../editor_menu/editor_menu.tsx | 147 --------------- .../workpad_header/editor_menu/index.ts | 9 - .../element_menu/element_menu.component.tsx | 6 +- .../workpad_header/element_menu/index.ts | 3 +- .../workpad_header.component.tsx | 28 +-- x-pack/plugins/canvas/public/plugin.tsx | 9 +- .../routes/workpad/hooks/use_workpad.ts | 18 +- .../canvas/public/services/embeddables.ts | 6 +- .../plugins/canvas/public/services/index.ts | 4 - .../public/services/kibana/embeddables.ts | 1 - .../canvas/public/services/kibana/index.ts | 3 - .../public/services/kibana/visualizations.ts | 21 --- .../public/services/stubs/embeddables.ts | 1 - .../canvas/public/services/stubs/index.ts | 3 - .../public/services/stubs/visualizations.ts | 19 -- .../canvas/public/services/visualizations.ts | 14 -- .../public/state/reducers/embeddable.ts | 2 +- x-pack/plugins/canvas/server/plugin.ts | 9 +- .../canvas/server/setup_interpreter.ts | 12 +- x-pack/plugins/canvas/types/embeddables.ts | 16 -- x-pack/plugins/canvas/types/functions.ts | 10 +- x-pack/plugins/canvas/types/index.ts | 1 - 60 files changed, 131 insertions(+), 1346 deletions(-) delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts delete mode 100644 x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts delete mode 100644 x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts delete mode 100644 x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts delete mode 100644 x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx delete mode 100644 x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts delete mode 100644 x-pack/plugins/canvas/public/services/kibana/visualizations.ts delete mode 100644 x-pack/plugins/canvas/public/services/stubs/visualizations.ts delete mode 100644 x-pack/plugins/canvas/public/services/visualizations.ts delete mode 100644 x-pack/plugins/canvas/types/embeddables.ts diff --git a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx index effbf8ce980d7..8a46a16c1bf0c 100644 --- a/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx +++ b/src/plugins/dashboard/public/application/top_nav/editor_menu.tsx @@ -231,7 +231,7 @@ export const EditorMenu = ({ dashboardContainer, createNewVisType }: Props) => { = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, - 'labs:canvas:byValueEmbeddable': { - type: 'boolean', - _meta: { description: 'Non-default value of setting.' }, - }, 'labs:canvas:useDataService': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 53d651b022529..9dcd2038edb9d 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -121,7 +121,6 @@ export interface UsageStats { 'banners:textColor': string; 'banners:backgroundColor': string; 'labs:canvas:enable_ui': boolean; - 'labs:canvas:byValueEmbeddable': boolean; 'labs:canvas:useDataService': boolean; 'labs:presentation:timeToPresent': boolean; 'labs:dashboard:enable_ui': boolean; diff --git a/src/plugins/presentation_util/common/labs.ts b/src/plugins/presentation_util/common/labs.ts index 8eefbd6981280..cb976e73b5edf 100644 --- a/src/plugins/presentation_util/common/labs.ts +++ b/src/plugins/presentation_util/common/labs.ts @@ -11,9 +11,7 @@ import { i18n } from '@kbn/i18n'; export const LABS_PROJECT_PREFIX = 'labs:'; export const DEFER_BELOW_FOLD = `${LABS_PROJECT_PREFIX}dashboard:deferBelowFold` as const; export const DASHBOARD_CONTROLS = `${LABS_PROJECT_PREFIX}dashboard:dashboardControls` as const; -export const BY_VALUE_EMBEDDABLE = `${LABS_PROJECT_PREFIX}canvas:byValueEmbeddable` as const; - -export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS, BY_VALUE_EMBEDDABLE] as const; +export const projectIDs = [DEFER_BELOW_FOLD, DASHBOARD_CONTROLS] as const; export const environmentNames = ['kibana', 'browser', 'session'] as const; export const solutionNames = ['canvas', 'dashboard', 'presentation'] as const; @@ -50,19 +48,6 @@ export const projects: { [ID in ProjectID]: ProjectConfig & { id: ID } } = { }), solutions: ['dashboard'], }, - [BY_VALUE_EMBEDDABLE]: { - id: BY_VALUE_EMBEDDABLE, - isActive: true, - isDisplayed: true, - environments: ['kibana', 'browser', 'session'], - name: i18n.translate('presentationUtil.labs.enableByValueEmbeddableName', { - defaultMessage: 'By-Value Embeddables', - }), - description: i18n.translate('presentationUtil.labs.enableByValueEmbeddableDescription', { - defaultMessage: 'Enables support for by-value embeddables in Canvas', - }), - solutions: ['canvas'], - }, }; export type ProjectID = typeof projectIDs[number]; diff --git a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss index c70e317546d40..535570a51d777 100644 --- a/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss +++ b/src/plugins/presentation_util/public/components/solution_toolbar/items/quick_group.scss @@ -1,31 +1,11 @@ .quickButtonGroup { - .euiButtonGroup__buttons { - border-radius: $euiBorderRadius; - - .quickButtonGroup__button { - background-color: $euiColorEmptyShade; - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-width: $euiBorderWidthThin !important; - border-style: solid !important; - border-color: $euiBorderColor !important; - } - } - - .quickButtonGroup__button:first-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-left-radius: $euiBorderRadius !important; - border-bottom-left-radius: $euiBorderRadius !important; - } - } - - .quickButtonGroup__button:last-of-type { - @include kbnThemeStyle('v8') { - // sass-lint:disable-block no-important - border-top-right-radius: $euiBorderRadius !important; - border-bottom-right-radius: $euiBorderRadius !important; - } + .quickButtonGroup__button { + background-color: $euiColorEmptyShade; + @include kbnThemeStyle('v8') { + // sass-lint:disable-block no-important + border-width: $euiBorderWidthThin !important; + border-style: solid !important; + border-color: $euiBorderColor !important; } } } diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index e7c18966ce0c0..251fed955788e 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -7677,12 +7677,6 @@ "description": "Non-default value of setting." } }, - "labs:canvas:byValueEmbeddable": { - "type": "boolean", - "_meta": { - "description": "Non-default value of setting." - } - }, "labs:canvas:useDataService": { "type": "boolean", "_meta": { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts index f1ede936c6ace..ac2e8e8babee1 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/expression_types/embeddable.ts @@ -6,11 +6,12 @@ */ import { ExpressionTypeDefinition } from '../../../../../src/plugins/expressions'; -import { EmbeddableInput } from '../../types'; +import { EmbeddableInput } from '../../../../../src/plugins/embeddable/common/'; import { EmbeddableTypes } from './embeddable_types'; export const EmbeddableExpressionType = 'embeddable'; export { EmbeddableTypes, EmbeddableInput }; + export interface EmbeddableExpression { /** * The type of the expression result diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts index d6d7a0f867849..2cfdebafb70df 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/index.ts @@ -6,6 +6,7 @@ */ import { functions as commonFunctions } from '../common'; +import { functions as externalFunctions } from '../external'; import { location } from './location'; import { markdown } from './markdown'; import { urlparam } from './urlparam'; @@ -13,4 +14,13 @@ import { escount } from './escount'; import { esdocs } from './esdocs'; import { essql } from './essql'; -export const functions = [location, markdown, urlparam, escount, esdocs, essql, ...commonFunctions]; +export const functions = [ + location, + markdown, + urlparam, + escount, + esdocs, + essql, + ...commonFunctions, + ...externalFunctions, +]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts deleted file mode 100644 index 001fb0e3f62e3..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { embeddableFunctionFactory } from './embeddable'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter } from '../../../types'; -import { encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -const filterContext: ExpressionValueFilter = { - type: 'filter', - and: [ - { - type: 'filter', - and: [], - value: 'filter-value', - column: 'filter-column', - filterType: 'exactly', - }, - { - type: 'filter', - and: [], - column: 'time-column', - filterType: 'time', - from: '2019-06-04T04:00:00.000Z', - to: '2019-06-05T04:00:00.000Z', - }, - ], -}; - -describe('embeddable', () => { - const fn = embeddableFunctionFactory({} as InitializeArguments)().fn; - const config = { - id: 'some-id', - timerange: { from: '15m', to: 'now' }, - title: 'test embeddable', - }; - - const args = { - config: encode(config), - type: 'visualization', - }; - - it('accepts null context', () => { - const expression = fn(null, args, {} as any); - - expect(expression.input.filters).toEqual([]); - }); - - it('accepts filter context', () => { - const expression = fn(filterContext, args, {} as any); - const embeddableFilters = getQueryFilters(filterContext.and); - - expect(expression.input.filters).toEqual(embeddableFilters); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts deleted file mode 100644 index 7ef8f0a09eb90..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/embeddable.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { ExpressionValueFilter, EmbeddableInput } from '../../../types'; -import { EmbeddableExpressionType, EmbeddableExpression } from '../../expression_types'; -import { getFunctionHelp } from '../../../i18n'; -import { SavedObjectReference } from '../../../../../../src/core/types'; -import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { decode, encode } from '../../../common/lib/embeddable_dataurl'; -import { InitializeArguments } from '.'; - -export interface Arguments { - config: string; - type: string; -} - -const defaultTimeRange = { - from: 'now-15m', - to: 'now', -}; - -const baseEmbeddableInput = { - timeRange: defaultTimeRange, - disableTriggers: true, - renderMode: 'noInteractivity', -}; - -type Return = EmbeddableExpression; - -type EmbeddableFunction = ExpressionFunctionDefinition< - 'embeddable', - ExpressionValueFilter | null, - Arguments, - Return ->; - -export function embeddableFunctionFactory({ - embeddablePersistableStateService, -}: InitializeArguments): () => EmbeddableFunction { - return function embeddable(): EmbeddableFunction { - const { help, args: argHelp } = getFunctionHelp().embeddable; - - return { - name: 'embeddable', - help, - args: { - config: { - aliases: ['_'], - types: ['string'], - required: true, - help: argHelp.config, - }, - type: { - types: ['string'], - required: true, - help: argHelp.type, - }, - }, - context: { - types: ['filter'], - }, - type: EmbeddableExpressionType, - fn: (input, args) => { - const filters = input ? input.and : []; - - const embeddableInput = decode(args.config) as EmbeddableInput; - - return { - type: EmbeddableExpressionType, - input: { - ...baseEmbeddableInput, - ...embeddableInput, - filters: getQueryFilters(filters), - }, - generatedAt: Date.now(), - embeddableType: args.type, - }; - }, - - extract(state) { - const input = decode(state.config[0] as string); - - // extracts references for by-reference embeddables - if (input.savedObjectId) { - const refName = 'embeddable.savedObjectId'; - - const references: SavedObjectReference[] = [ - { - name: refName, - type: state.type[0] as string, - id: input.savedObjectId as string, - }, - ]; - - return { - state, - references, - }; - } - - // extracts references for by-value embeddables - const { state: extractedState, references: extractedReferences } = - embeddablePersistableStateService.extract({ - ...input, - type: state.type[0], - }); - - const { type, ...extractedInput } = extractedState; - - return { - state: { ...state, config: [encode(extractedInput)], type: [type] }, - references: extractedReferences, - }; - }, - - inject(state, references) { - const input = decode(state.config[0] as string); - const savedObjectReference = references.find( - (ref) => ref.name === 'embeddable.savedObjectId' - ); - - // injects saved object id for by-references embeddable - if (savedObjectReference) { - input.savedObjectId = savedObjectReference.id; - state.config[0] = encode(input); - state.type[0] = savedObjectReference.type; - } else { - // injects references for by-value embeddables - const { type, ...injectedInput } = embeddablePersistableStateService.inject( - { ...input, type: state.type[0] }, - references - ); - state.config[0] = encode(injectedInput); - state.type[0] = type; - } - return state; - }, - }; - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts index 1d69e181b5fd9..407a0e2ebfe05 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/index.ts @@ -5,26 +5,9 @@ * 2.0. */ -import { EmbeddableStart } from 'src/plugins/embeddable/public'; -import { embeddableFunctionFactory } from './embeddable'; import { savedLens } from './saved_lens'; import { savedMap } from './saved_map'; import { savedSearch } from './saved_search'; import { savedVisualization } from './saved_visualization'; -export interface InitializeArguments { - embeddablePersistableStateService: { - extract: EmbeddableStart['extract']; - inject: EmbeddableStart['inject']; - }; -} - -export function initFunctions(initialize: InitializeArguments) { - return [ - embeddableFunctionFactory(initialize), - savedLens, - savedMap, - savedSearch, - savedVisualization, - ]; -} +export const functions = [savedLens, savedMap, savedVisualization, savedSearch]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts index 67947691f7757..082a69a874cae 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.ts @@ -9,8 +9,9 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { PaletteOutput } from 'src/plugins/charts/common'; import { Filter as DataFilter } from '@kbn/es-query'; import { TimeRange } from 'src/plugins/data/common'; +import { EmbeddableInput } from 'src/plugins/embeddable/common'; import { getQueryFilters } from '../../../common/lib/build_embeddable_filters'; -import { ExpressionValueFilter, EmbeddableInput, TimeRange as TimeRangeArg } from '../../../types'; +import { ExpressionValueFilter, TimeRange as TimeRangeArg } from '../../../types'; import { EmbeddableTypes, EmbeddableExpressionType, @@ -26,7 +27,7 @@ interface Arguments { } export type SavedLensInput = EmbeddableInput & { - savedObjectId: string; + id: string; timeRange?: TimeRange; filters: DataFilter[]; palette?: PaletteOutput; @@ -72,19 +73,18 @@ export function savedLens(): ExpressionFunctionDefinition< }, }, type: EmbeddableExpressionType, - fn: (input, { id, timerange, title, palette }) => { + fn: (input, args) => { const filters = input ? input.and : []; return { type: EmbeddableExpressionType, input: { - id, - savedObjectId: id, + id: args.id, filters: getQueryFilters(filters), - timeRange: timerange || defaultTimeRange, - title: title === null ? undefined : title, + timeRange: args.timerange || defaultTimeRange, + title: args.title === null ? undefined : args.title, disableTriggers: true, - palette, + palette: args.palette, }, embeddableType: EmbeddableTypes.lens, generatedAt: Date.now(), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts index a7471c755155c..538ed3f919823 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.ts @@ -30,7 +30,7 @@ const defaultTimeRange = { to: 'now', }; -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; export function savedMap(): ExpressionFunctionDefinition< 'savedMap', @@ -85,9 +85,8 @@ export function savedMap(): ExpressionFunctionDefinition< return { type: EmbeddableExpressionType, input: { - id: args.id, attributes: { title: '' }, - savedObjectId: args.id, + id: args.id, filters: getQueryFilters(filters), timeRange: args.timerange || defaultTimeRange, refreshConfig: { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts index 31e3fb2a8c564..5c0442b43250c 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.ts @@ -25,7 +25,7 @@ interface Arguments { title: string | null; } -type Output = EmbeddableExpression; +type Output = EmbeddableExpression; const defaultTimeRange = { from: 'now-15m', @@ -94,7 +94,6 @@ export function savedVisualization(): ExpressionFunctionDefinition< type: EmbeddableExpressionType, input: { id, - savedObjectId: id, disableTriggers: true, timeRange: timerange || defaultTimeRange, filters: getQueryFilters(filters), diff --git a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts index 591795637aebe..91c573fc4148b 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/plugin.ts @@ -7,14 +7,12 @@ import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; import { ChartsPluginStart } from 'src/plugins/charts/public'; -import { PresentationUtilPluginStart } from 'src/plugins/presentation_util/public'; import { CanvasSetup } from '../public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; import { functions } from './functions/browser'; -import { initFunctions } from './functions/external'; import { typeFunctions } from './expression_types'; import { renderFunctions, renderFunctionFactories } from './renderers'; @@ -27,7 +25,6 @@ export interface StartDeps { uiActions: UiActionsStart; inspector: InspectorStart; charts: ChartsPluginStart; - presentationUtil: PresentationUtilPluginStart; } export type SetupInitializer = (core: CoreSetup, plugins: SetupDeps) => T; @@ -42,13 +39,6 @@ export class CanvasSrcPlugin implements Plugin plugins.canvas.addRenderers(renderFunctions); core.getStartServices().then(([coreStart, depsStart]) => { - const externalFunctions = initFunctions({ - embeddablePersistableStateService: { - extract: depsStart.embeddable.extract, - inject: depsStart.embeddable.inject, - }, - }); - plugins.canvas.addFunctions(externalFunctions); plugins.canvas.addRenderers( renderFunctionFactories.map((factory: any) => factory(coreStart, depsStart)) ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx index 953746c280840..73e839433c25e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.tsx @@ -13,17 +13,16 @@ import { IEmbeddable, EmbeddableFactory, EmbeddableFactoryNotFoundError, - isErrorEmbeddable, } from '../../../../../../src/plugins/embeddable/public'; import { EmbeddableExpression } from '../../expression_types/embeddable'; import { RendererStrings } from '../../../i18n'; import { embeddableInputToExpression } from './embeddable_input_to_expression'; -import { RendererFactory, EmbeddableInput } from '../../../types'; +import { EmbeddableInput } from '../../expression_types'; +import { RendererFactory } from '../../../types'; import { CANVAS_EMBEDDABLE_CLASSNAME } from '../../../common/lib'; const { embeddable: strings } = RendererStrings; -// registry of references to embeddables on the workpad const embeddablesRegistry: { [key: string]: IEmbeddable | Promise; } = {}; @@ -31,11 +30,11 @@ const embeddablesRegistry: { const renderEmbeddableFactory = (core: CoreStart, plugins: StartDeps) => { const I18nContext = core.i18n.Context; - return (embeddableObject: IEmbeddable) => { + return (embeddableObject: IEmbeddable, domNode: HTMLElement) => { return (
@@ -57,9 +56,6 @@ export const embeddableRendererFactory = ( reuseDomNode: true, render: async (domNode, { input, embeddableType }, handlers) => { const uniqueId = handlers.getElementId(); - const isByValueEnabled = plugins.presentationUtil.labsService.isProjectEnabled( - 'labs:canvas:byValueEmbeddable' - ); if (!embeddablesRegistry[uniqueId]) { const factory = Array.from(plugins.embeddable.getEmbeddableFactories()).find( @@ -71,27 +67,15 @@ export const embeddableRendererFactory = ( throw new EmbeddableFactoryNotFoundError(embeddableType); } - const embeddableInput = { ...input, id: uniqueId }; - - const embeddablePromise = input.savedObjectId - ? factory - .createFromSavedObject(input.savedObjectId, embeddableInput) - .then((embeddable) => { - // stores embeddable in registrey - embeddablesRegistry[uniqueId] = embeddable; - return embeddable; - }) - : factory.create(embeddableInput).then((embeddable) => { - if (!embeddable || isErrorEmbeddable(embeddable)) { - return; - } - // stores embeddable in registry - embeddablesRegistry[uniqueId] = embeddable as IEmbeddable; - return embeddable; - }); - embeddablesRegistry[uniqueId] = embeddablePromise as Promise; - - const embeddableObject = (await (async () => embeddablePromise)()) as IEmbeddable; + const embeddablePromise = factory + .createFromSavedObject(input.id, input) + .then((embeddable) => { + embeddablesRegistry[uniqueId] = embeddable; + return embeddable; + }); + embeddablesRegistry[uniqueId] = embeddablePromise; + + const embeddableObject = await (async () => embeddablePromise)(); const palettes = await plugins.charts.palettes.getPalettes(); @@ -102,8 +86,7 @@ export const embeddableRendererFactory = ( const updatedExpression = embeddableInputToExpression( updatedInput, embeddableType, - palettes, - isByValueEnabled + palettes ); if (updatedExpression) { @@ -111,7 +94,15 @@ export const embeddableRendererFactory = ( } }); - ReactDOM.render(renderEmbeddable(embeddableObject), domNode, () => handlers.done()); + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + + handlers.onResize(() => { + ReactDOM.render(renderEmbeddable(embeddableObject, domNode), domNode, () => + handlers.done() + ); + }); handlers.onDestroy(() => { subscription.unsubscribe(); @@ -124,7 +115,6 @@ export const embeddableRendererFactory = ( } else { const embeddable = embeddablesRegistry[uniqueId]; - // updating embeddable input with changes made to expression or filters if ('updateInput' in embeddable) { embeddable.updateInput(input); embeddable.reload(); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts index 80830eac24021..41cefad6a470f 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable_input_to_expression.ts @@ -10,7 +10,6 @@ import { EmbeddableTypes, EmbeddableInput } from '../../expression_types'; import { toExpression as mapToExpression } from './input_type_to_expression/map'; import { toExpression as visualizationToExpression } from './input_type_to_expression/visualization'; import { toExpression as lensToExpression } from './input_type_to_expression/lens'; -import { toExpression as genericToExpression } from './input_type_to_expression/embeddable'; export const inputToExpressionTypeMap = { [EmbeddableTypes.map]: mapToExpression, @@ -24,13 +23,8 @@ export const inputToExpressionTypeMap = { export function embeddableInputToExpression( input: EmbeddableInput, embeddableType: string, - palettes: PaletteRegistry, - useGenericEmbeddable?: boolean + palettes: PaletteRegistry ): string | undefined { - if (useGenericEmbeddable) { - return genericToExpression(input, embeddableType); - } - if (inputToExpressionTypeMap[embeddableType]) { return inputToExpressionTypeMap[embeddableType](input as any, palettes); } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.test.ts deleted file mode 100644 index 4b78acec8750a..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { toExpression } from './embeddable'; -import { EmbeddableInput } from '../../../../types'; -import { decode } from '../../../../common/lib/embeddable_dataurl'; -import { fromExpression } from '@kbn/interpreter/common'; - -describe('toExpression', () => { - describe('by-reference embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', - filters: [], - }; - - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config.savedObjectId).toStrictEqual(input.savedObjectId); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); - - describe('by-value embeddable input', () => { - const baseEmbeddableInput = { - id: 'elementId', - disableTriggers: true, - filters: [], - }; - it('converts to an embeddable expression', () => { - const input: EmbeddableInput = baseEmbeddableInput; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - expect(ast.type).toBe('expression'); - expect(ast.chain[0].function).toBe('embeddable'); - expect(ast.chain[0].arguments.type[0]).toBe('visualization'); - - const config = decode(ast.chain[0].arguments.config[0] as string); - expect(config.filters).toStrictEqual(input.filters); - expect(config.disableTriggers).toStrictEqual(input.disableTriggers); - }); - - it('includes optional input values', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: 'title', - timeRange: { - from: 'now-1h', - to: 'now', - }, - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - expect(config).toHaveProperty('timeRange'); - expect(config.timeRange).toHaveProperty('from', input.timeRange?.from); - expect(config.timeRange).toHaveProperty('to', input.timeRange?.to); - }); - - it('includes empty panel title', () => { - const input: EmbeddableInput = { - ...baseEmbeddableInput, - title: '', - }; - - const expression = toExpression(input, 'visualization'); - const ast = fromExpression(expression); - - const config = decode(ast.chain[0].arguments.config[0] as string); - - expect(config).toHaveProperty('title', input.title); - }); - }); -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts deleted file mode 100644 index 94d86f6640be1..0000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/embeddable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { encode } from '../../../../common/lib/embeddable_dataurl'; -import { EmbeddableInput } from '../../../expression_types'; - -export function toExpression(input: EmbeddableInput, embeddableType: string): string { - return `embeddable config="${encode(input)}" type="${embeddableType}"`; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts index 224cdfba389d7..24da7238bcee9 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.test.ts @@ -11,8 +11,7 @@ import { fromExpression, Ast } from '@kbn/interpreter/common'; import { chartPluginMock } from 'src/plugins/charts/public/mocks'; const baseEmbeddableInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], }; @@ -28,7 +27,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedLens'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('timerange'); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts index 5a13b73b3fe74..35e106f234fa4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/lens.ts @@ -14,7 +14,7 @@ export function toExpression(input: SavedLensInput, palettes: PaletteRegistry): expressionParts.push('savedLens'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts index af7b40a9b283d..804d0d849cc7f 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.test.ts @@ -6,12 +6,12 @@ */ import { toExpression } from './map'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseSavedMapInput = { - id: 'elementId', attributes: { title: '' }, - savedObjectId: 'embeddableId', + id: 'embeddableId', filters: [], isLayerTOCOpen: false, refreshConfig: { @@ -23,7 +23,7 @@ const baseSavedMapInput = { describe('toExpression', () => { it('converts to a savedMap expression', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, }; @@ -33,7 +33,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedMap'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); expect(ast.chain[0].arguments).not.toHaveProperty('title'); expect(ast.chain[0].arguments).not.toHaveProperty('center'); @@ -41,7 +41,7 @@ describe('toExpression', () => { }); it('includes optional input values', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, mapCenter: { lat: 1, @@ -73,7 +73,7 @@ describe('toExpression', () => { }); it('includes empty panel title', () => { - const input = { + const input: MapEmbeddableInput = { ...baseSavedMapInput, title: '', }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts index 03746f38b4696..3fd6a68a327c6 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/map.ts @@ -5,14 +5,13 @@ * 2.0. */ -import { MapEmbeddableInput } from '../../../../../../plugins/maps/public'; +import { MapEmbeddableInput } from '../../../../../../plugins/maps/public/embeddable'; -export function toExpression(input: MapEmbeddableInput & { savedObjectId: string }): string { +export function toExpression(input: MapEmbeddableInput): string { const expressionParts = [] as string[]; expressionParts.push('savedMap'); - - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts index 4c61a130f3c95..c5106b9a102b4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.test.ts @@ -9,8 +9,7 @@ import { toExpression } from './visualization'; import { fromExpression, Ast } from '@kbn/interpreter/common'; const baseInput = { - id: 'elementId', - savedObjectId: 'embeddableId', + id: 'embeddableId', }; describe('toExpression', () => { @@ -25,7 +24,7 @@ describe('toExpression', () => { expect(ast.type).toBe('expression'); expect(ast.chain[0].function).toBe('savedVisualization'); - expect(ast.chain[0].arguments.id).toStrictEqual([input.savedObjectId]); + expect(ast.chain[0].arguments.id).toStrictEqual([input.id]); }); it('includes timerange if given', () => { diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts index 364d7cd0755db..bcb73b2081fee 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/embeddable/input_type_to_expression/visualization.ts @@ -7,11 +7,11 @@ import { VisualizeInput } from 'src/plugins/visualizations/public'; -export function toExpression(input: VisualizeInput & { savedObjectId: string }): string { +export function toExpression(input: VisualizeInput): string { const expressionParts = [] as string[]; expressionParts.push('savedVisualization'); - expressionParts.push(`id="${input.savedObjectId}"`); + expressionParts.push(`id="${input.id}"`); if (input.title !== undefined) { expressionParts.push(`title="${input.title}"`); diff --git a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts b/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts deleted file mode 100644 index e76dedfe63b14..0000000000000 --- a/x-pack/plugins/canvas/common/lib/embeddable_dataurl.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EmbeddableInput } from '../../types'; - -export const encode = (input: Partial) => - Buffer.from(JSON.stringify(input)).toString('base64'); -export const decode = (serializedInput: string) => - JSON.parse(Buffer.from(serializedInput, 'base64').toString()); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts b/x-pack/plugins/canvas/i18n/functions/dict/embeddable.ts deleted file mode 100644 index 279f58799e8c0..0000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/embeddable.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { embeddableFunctionFactory } from '../../../canvas_plugin_src/functions/external/embeddable'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; - -export const help: FunctionHelp>> = { - help: i18n.translate('xpack.canvas.functions.embeddableHelpText', { - defaultMessage: `Returns an embeddable with the provided configuration`, - }), - args: { - config: i18n.translate('xpack.canvas.functions.embeddable.args.idHelpText', { - defaultMessage: `The base64 encoded embeddable input object`, - }), - type: i18n.translate('xpack.canvas.functions.embeddable.args.typeHelpText', { - defaultMessage: `The embeddable type`, - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index 520d32af1c272..5eae785fefa2e 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -27,7 +27,6 @@ import { help as demodata } from './dict/demodata'; import { help as doFn } from './dict/do'; import { help as dropdownControl } from './dict/dropdown_control'; import { help as eq } from './dict/eq'; -import { help as embeddable } from './dict/embeddable'; import { help as escount } from './dict/escount'; import { help as esdocs } from './dict/esdocs'; import { help as essql } from './dict/essql'; @@ -183,7 +182,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ do: doFn, dropdownControl, eq, - embeddable, escount, esdocs, essql, diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 2fd312502a3c7..9c4d1b2179d82 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -25,7 +25,6 @@ "features", "inspector", "presentationUtil", - "visualizations", "uiActions", "share" ], diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx index 57f52fcf21f0f..bf731876bf8c8 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.component.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { FC, useCallback } from 'react'; +import React, { FC } from 'react'; import { EuiFlyout, EuiFlyoutHeader, EuiFlyoutBody, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,44 +27,38 @@ const strings = { }; export interface Props { onClose: () => void; - onSelect: (id: string, embeddableType: string, isByValueEnabled?: boolean) => void; + onSelect: (id: string, embeddableType: string) => void; availableEmbeddables: string[]; - isByValueEnabled?: boolean; } -export const AddEmbeddableFlyout: FC = ({ - onSelect, - availableEmbeddables, - onClose, - isByValueEnabled, -}) => { +export const AddEmbeddableFlyout: FC = ({ onSelect, availableEmbeddables, onClose }) => { const embeddablesService = useEmbeddablesService(); const platformService = usePlatformService(); const { getEmbeddableFactories } = embeddablesService; const { getSavedObjects, getUISettings } = platformService; - const onAddPanel = useCallback( - (id: string, savedObjectType: string) => { - const embeddableFactories = getEmbeddableFactories(); - // Find the embeddable type from the saved object type - const found = Array.from(embeddableFactories).find((embeddableFactory) => { - return Boolean( - embeddableFactory.savedObjectMetaData && - embeddableFactory.savedObjectMetaData.type === savedObjectType - ); - }); + const onAddPanel = (id: string, savedObjectType: string, name: string) => { + const embeddableFactories = getEmbeddableFactories(); - const foundEmbeddableType = found ? found.type : 'unknown'; + // Find the embeddable type from the saved object type + const found = Array.from(embeddableFactories).find((embeddableFactory) => { + return Boolean( + embeddableFactory.savedObjectMetaData && + embeddableFactory.savedObjectMetaData.type === savedObjectType + ); + }); - onSelect(id, foundEmbeddableType, isByValueEnabled); - }, - [isByValueEnabled, getEmbeddableFactories, onSelect] - ); + const foundEmbeddableType = found ? found.type : 'unknown'; + + onSelect(id, foundEmbeddableType); + }; const embeddableFactories = getEmbeddableFactories(); const availableSavedObjects = Array.from(embeddableFactories) - .filter((factory) => isByValueEnabled || availableEmbeddables.includes(factory.type)) + .filter((factory) => { + return availableEmbeddables.includes(factory.type); + }) .map((factory) => factory.savedObjectMetaData) .filter>(function ( maybeSavedObjectMetaData diff --git a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx index 4dc8d963932d8..770a4cac606b0 100644 --- a/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx +++ b/x-pack/plugins/canvas/public/components/embeddable_flyout/flyout.tsx @@ -8,14 +8,12 @@ import React, { useMemo, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; import { useSelector, useDispatch } from 'react-redux'; -import { encode } from '../../../common/lib/embeddable_dataurl'; import { AddEmbeddableFlyout as Component, Props as ComponentProps } from './flyout.component'; // @ts-expect-error untyped local import { addElement } from '../../state/actions/elements'; import { getSelectedPage } from '../../state/selectors/workpad'; import { EmbeddableTypes } from '../../../canvas_plugin_src/expression_types/embeddable'; import { State } from '../../../types'; -import { useLabsService } from '../../services'; const allowedEmbeddables = { [EmbeddableTypes.map]: (id: string) => { @@ -67,9 +65,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ availableEmbeddables, ...restProps }) => { - const labsService = useLabsService(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const dispatch = useDispatch(); const pageId = useSelector((state) => getSelectedPage(state)); @@ -79,27 +74,18 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ ); const onSelect = useCallback( - (id: string, type: string): void => { + (id: string, type: string) => { const partialElement = { expression: `markdown "Could not find embeddable for type ${type}" | render`, }; - - // If by-value is enabled, we'll handle both by-reference and by-value embeddables - // with the new generic `embeddable` function. - // Otherwise we fallback to the embeddable type specific expressions. - if (isByValueEnabled) { - const config = encode({ savedObjectId: id }); - partialElement.expression = `embeddable config="${config}" - type="${type}" -| render`; - } else if (allowedEmbeddables[type]) { + if (allowedEmbeddables[type]) { partialElement.expression = allowedEmbeddables[type](id); } addEmbeddable(pageId, partialElement); restProps.onClose(); }, - [addEmbeddable, pageId, restProps, isByValueEnabled] + [addEmbeddable, pageId, restProps] ); return ( @@ -107,7 +93,6 @@ export const AddEmbeddablePanel: React.FunctionComponent = ({ {...restProps} availableEmbeddables={availableEmbeddables || []} onSelect={onSelect} - isByValueEnabled={isByValueEnabled} /> ); }; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx index ffd5b095b12e5..50d527036560a 100644 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx +++ b/x-pack/plugins/canvas/public/components/hooks/workpad/index.tsx @@ -6,5 +6,3 @@ */ export { useDownloadWorkpad, useDownloadRenderedWorkpad } from './use_download_workpad'; - -export { useIncomingEmbeddable } from './use_incoming_embeddable'; diff --git a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts b/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.ts deleted file mode 100644 index 2f8e2503ea57e..0000000000000 --- a/x-pack/plugins/canvas/public/components/hooks/workpad/use_incoming_embeddable.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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { fromExpression } from '@kbn/interpreter/common'; -import { CANVAS_APP } from '../../../../common/lib'; -import { decode, encode } from '../../../../common/lib/embeddable_dataurl'; -import { CanvasElement, CanvasPage } from '../../../../types'; -import { useEmbeddablesService, useLabsService } from '../../../services'; -// @ts-expect-error unconverted file -import { addElement } from '../../../state/actions/elements'; -// @ts-expect-error unconverted file -import { selectToplevelNodes } from '../../../state/actions/transient'; - -import { - updateEmbeddableExpression, - fetchEmbeddableRenderable, -} from '../../../state/actions/embeddable'; -import { clearValue } from '../../../state/actions/resolved_args'; - -export const useIncomingEmbeddable = (selectedPage: CanvasPage) => { - const embeddablesService = useEmbeddablesService(); - const labsService = useLabsService(); - const dispatch = useDispatch(); - const isByValueEnabled = labsService.isProjectEnabled('labs:canvas:byValueEmbeddable'); - const stateTransferService = embeddablesService.getStateTransfer(); - - // fetch incoming embeddable from state transfer service. - const incomingEmbeddable = stateTransferService.getIncomingEmbeddablePackage(CANVAS_APP, true); - - useEffect(() => { - if (isByValueEnabled && incomingEmbeddable) { - const { embeddableId, input: incomingInput, type } = incomingEmbeddable; - - // retrieve existing element - const originalElement = selectedPage.elements.find( - ({ id }: CanvasElement) => id === embeddableId - ); - - if (originalElement) { - const originalAst = fromExpression(originalElement!.expression); - - const functionIndex = originalAst.chain.findIndex( - ({ function: fn }) => fn === 'embeddable' - ); - - const originalInput = decode( - originalAst.chain[functionIndex].arguments.config[0] as string - ); - - // clear out resolved arg for old embeddable - const argumentPath = [embeddableId, 'expressionRenderable']; - dispatch(clearValue({ path: argumentPath })); - - const updatedInput = { ...originalInput, ...incomingInput }; - - const expression = `embeddable config="${encode(updatedInput)}" - type="${type}" -| render`; - - dispatch( - updateEmbeddableExpression({ - elementId: originalElement.id, - embeddableExpression: expression, - }) - ); - - // update resolved args - dispatch(fetchEmbeddableRenderable(originalElement.id)); - - // select new embeddable element - dispatch(selectToplevelNodes([embeddableId])); - } else { - const expression = `embeddable config="${encode(incomingInput)}" - type="${type}" -| render`; - dispatch(addElement(selectedPage.id, { expression })); - } - } - }, [dispatch, selectedPage, incomingEmbeddable, isByValueEnabled]); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx index 7cc077203c737..622c885b6ef28 100644 --- a/x-pack/plugins/canvas/public/components/workpad/workpad.tsx +++ b/x-pack/plugins/canvas/public/components/workpad/workpad.tsx @@ -27,7 +27,6 @@ import { WorkpadRoutingContext } from '../../routes/workpad'; import { usePlatformService } from '../../services'; import { Workpad as WorkpadComponent, Props } from './workpad.component'; import { State } from '../../../types'; -import { useIncomingEmbeddable } from '../hooks'; type ContainerProps = Pick; @@ -59,9 +58,6 @@ export const Workpad: FC = (props) => { }; }); - const selectedPage = propsFromState.pages[propsFromState.selectedPageNumber - 1]; - useIncomingEmbeddable(selectedPage); - const fetchAllRenderables = useCallback(() => { dispatch(fetchAllRenderablesAction()); }, [dispatch]); diff --git a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss index 0ddd44ed8f9a8..4acdca10d61cc 100644 --- a/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss +++ b/x-pack/plugins/canvas/public/components/workpad_app/workpad_app.scss @@ -31,7 +31,7 @@ $canvasLayoutFontSize: $euiFontSizeS; .canvasLayout__stageHeader { flex-grow: 0; flex-basis: auto; - padding: $euiSizeS $euiSize; + padding: $euiSizeS; font-size: $canvasLayoutFontSize; border-bottom: $euiBorderThin; background: $euiColorLightestShade; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot deleted file mode 100644 index f4aab0e59e7ee..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/__snapshots__/editor_menu.stories.storyshot +++ /dev/null @@ -1,81 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Storyshots components/WorkpadHeader/EditorMenu dark mode 1`] = ` -
-
- -
-
-`; - -exports[`Storyshots components/WorkpadHeader/EditorMenu default 1`] = ` -
-
- -
-
-`; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx deleted file mode 100644 index 01048bc0af301..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/__stories__/editor_menu.stories.tsx +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { storiesOf } from '@storybook/react'; -import { action } from '@storybook/addon-actions'; -import React from 'react'; -import { EmbeddableFactoryDefinition, IEmbeddable } from 'src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from 'src/plugins/visualizations/public'; -import { EditorMenu } from '../editor_menu.component'; - -const testFactories: EmbeddableFactoryDefinition[] = [ - { - type: 'ml_anomaly_swimlane', - getDisplayName: () => 'Anomaly swimlane', - getIconType: () => '', - getDescription: () => 'Description for anomaly swimlane', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'swimlane_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'ml_anomaly_chart', - getDisplayName: () => 'Anomaly chart', - getIconType: () => '', - getDescription: () => 'Description for anomaly chart', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - grouping: [ - { - id: 'ml', - getDisplayName: () => 'machine learning', - getIconType: () => 'machineLearningApp', - }, - ], - }, - { - type: 'log_stream', - getDisplayName: () => 'Log stream', - getIconType: () => '', - getDescription: () => 'Description for log stream', - isEditable: () => Promise.resolve(true), - create: () => Promise.resolve({ id: 'anomaly_chart_embeddable' } as IEmbeddable), - }, -]; - -const testVisTypes: BaseVisType[] = [ - { title: 'TSVB', icon: '', description: 'Description of TSVB', name: 'tsvb' } as BaseVisType, - { - titleInWizard: 'Custom visualization', - title: 'Vega', - icon: '', - description: 'Description of Vega', - name: 'vega', - } as BaseVisType, -]; - -const testVisTypeAliases: VisTypeAlias[] = [ - { - title: 'Lens', - aliasApp: 'lens', - aliasPath: 'path/to/lens', - icon: 'lensApp', - name: 'lens', - description: 'Description of Lens app', - stage: 'production', - }, - { - title: 'Maps', - aliasApp: 'maps', - aliasPath: 'path/to/maps', - icon: 'gisApp', - name: 'maps', - description: 'Description of Maps app', - stage: 'production', - }, -]; - -storiesOf('components/WorkpadHeader/EditorMenu', module) - .add('default', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )) - .add('dark mode', () => ( - action('createNewVisType')} - createNewEmbeddable={() => action('createNewEmbeddable')} - /> - )); diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx deleted file mode 100644 index e8f762f9731a1..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.component.tsx +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC } from 'react'; -import { - EuiContextMenu, - EuiContextMenuPanelItemDescriptor, - EuiContextMenuItemIcon, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { EmbeddableFactoryDefinition } from '../../../../../../../src/plugins/embeddable/public'; -import { BaseVisType, VisTypeAlias } from '../../../../../../../src/plugins/visualizations/public'; -import { SolutionToolbarPopover } from '../../../../../../../src/plugins/presentation_util/public'; - -const strings = { - getEditorMenuButtonLabel: () => - i18n.translate('xpack.canvas.solutionToolbar.editorMenuButtonLabel', { - defaultMessage: 'Select type', - }), -}; - -interface FactoryGroup { - id: string; - appName: string; - icon: EuiContextMenuItemIcon; - panelId: number; - factories: EmbeddableFactoryDefinition[]; -} - -interface Props { - factories: EmbeddableFactoryDefinition[]; - isDarkThemeEnabled?: boolean; - promotedVisTypes: BaseVisType[]; - visTypeAliases: VisTypeAlias[]; - createNewVisType: (visType?: BaseVisType | VisTypeAlias) => () => void; - createNewEmbeddable: (factory: EmbeddableFactoryDefinition) => () => void; -} - -export const EditorMenu: FC = ({ - factories, - isDarkThemeEnabled, - promotedVisTypes, - visTypeAliases, - createNewVisType, - createNewEmbeddable, -}: Props) => { - const factoryGroupMap: Record = {}; - const ungroupedFactories: EmbeddableFactoryDefinition[] = []; - - let panelCount = 1; - - // Maps factories with a group to create nested context menus for each group type - // and pushes ungrouped factories into a separate array - factories.forEach((factory: EmbeddableFactoryDefinition, index) => { - const { grouping } = factory; - - if (grouping) { - grouping.forEach((group) => { - if (factoryGroupMap[group.id]) { - factoryGroupMap[group.id].factories.push(factory); - } else { - factoryGroupMap[group.id] = { - id: group.id, - appName: group.getDisplayName ? group.getDisplayName({}) : group.id, - icon: (group.getIconType ? group.getIconType({}) : 'empty') as EuiContextMenuItemIcon, - factories: [factory], - panelId: panelCount, - }; - - panelCount++; - } - }); - } else { - ungroupedFactories.push(factory); - } - }); - - const getVisTypeMenuItem = (visType: BaseVisType): EuiContextMenuPanelItemDescriptor => { - const { name, title, titleInWizard, description, icon = 'empty' } = visType; - return { - name: titleInWizard || title, - icon: icon as string, - onClick: createNewVisType(visType), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getVisTypeAliasMenuItem = ( - visTypeAlias: VisTypeAlias - ): EuiContextMenuPanelItemDescriptor => { - const { name, title, description, icon = 'empty' } = visTypeAlias; - - return { - name: title, - icon, - onClick: createNewVisType(visTypeAlias), - 'data-test-subj': `visType-${name}`, - toolTipContent: description, - }; - }; - - const getEmbeddableFactoryMenuItem = ( - factory: EmbeddableFactoryDefinition - ): EuiContextMenuPanelItemDescriptor => { - const icon = factory?.getIconType ? factory.getIconType() : 'empty'; - - const toolTipContent = factory?.getDescription ? factory.getDescription() : undefined; - - return { - name: factory.getDisplayName(), - icon, - toolTipContent, - onClick: createNewEmbeddable(factory), - 'data-test-subj': `createNew-${factory.type}`, - }; - }; - - const editorMenuPanels = [ - { - id: 0, - items: [ - ...visTypeAliases.map(getVisTypeAliasMenuItem), - ...Object.values(factoryGroupMap).map(({ id, appName, icon, panelId }) => ({ - name: appName, - icon, - panel: panelId, - 'data-test-subj': `canvasEditorMenu-${id}Group`, - })), - ...ungroupedFactories.map(getEmbeddableFactoryMenuItem), - ...promotedVisTypes.map(getVisTypeMenuItem), - ], - }, - ...Object.values(factoryGroupMap).map( - ({ appName, panelId, factories: groupFactories }: FactoryGroup) => ({ - id: panelId, - title: appName, - items: groupFactories.map(getEmbeddableFactoryMenuItem), - }) - ), - ]; - - return ( - - {() => ( - - )} - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx deleted file mode 100644 index dad34e6983c5d..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/editor_menu.tsx +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC, useCallback } from 'react'; -import { useLocation } from 'react-router-dom'; -import { trackCanvasUiMetric, METRIC_TYPE } from '../../../../public/lib/ui_metric'; -import { - useEmbeddablesService, - usePlatformService, - useVisualizationsService, -} from '../../../services'; -import { - BaseVisType, - VisGroups, - VisTypeAlias, -} from '../../../../../../../src/plugins/visualizations/public'; -import { - EmbeddableFactoryDefinition, - EmbeddableInput, -} from '../../../../../../../src/plugins/embeddable/public'; -import { CANVAS_APP } from '../../../../common/lib'; -import { encode } from '../../../../common/lib/embeddable_dataurl'; -import { ElementSpec } from '../../../../types'; -import { EditorMenu as Component } from './editor_menu.component'; - -interface Props { - /** - * Handler for adding a selected element to the workpad - */ - addElement: (element: Partial) => void; -} - -export const EditorMenu: FC = ({ addElement }) => { - const embeddablesService = useEmbeddablesService(); - const { pathname, search } = useLocation(); - const platformService = usePlatformService(); - const stateTransferService = embeddablesService.getStateTransfer(); - const visualizationsService = useVisualizationsService(); - const IS_DARK_THEME = platformService.getUISetting('theme:darkMode'); - - const createNewVisType = useCallback( - (visType?: BaseVisType | VisTypeAlias) => () => { - let path = ''; - let appId = ''; - - if (visType) { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, `${visType.name}:create`); - } - - if ('aliasPath' in visType) { - appId = visType.aliasApp; - path = visType.aliasPath; - } else { - appId = 'visualize'; - path = `#/create?type=${encodeURIComponent(visType.name)}`; - } - } else { - appId = 'visualize'; - path = '#/create?'; - } - - stateTransferService.navigateToEditor(appId, { - path, - state: { - originatingApp: CANVAS_APP, - originatingPath: `#/${pathname}${search}`, - }, - }); - }, - [stateTransferService, pathname, search] - ); - - const createNewEmbeddable = useCallback( - (factory: EmbeddableFactoryDefinition) => async () => { - if (trackCanvasUiMetric) { - trackCanvasUiMetric(METRIC_TYPE.CLICK, factory.type); - } - let embeddableInput; - if (factory.getExplicitInput) { - embeddableInput = await factory.getExplicitInput(); - } else { - const newEmbeddable = await factory.create({} as EmbeddableInput); - embeddableInput = newEmbeddable?.getInput(); - } - - if (embeddableInput) { - const config = encode(embeddableInput); - const expression = `embeddable config="${config}" - type="${factory.type}" -| render`; - - addElement({ expression }); - } - }, - [addElement] - ); - - const getVisTypesByGroup = (group: VisGroups): BaseVisType[] => - visualizationsService - .getByGroup(group) - .sort(({ name: a }: BaseVisType | VisTypeAlias, { name: b }: BaseVisType | VisTypeAlias) => { - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; - }) - .filter(({ hidden }: BaseVisType) => !hidden); - - const visTypeAliases = visualizationsService - .getAliases() - .sort(({ promotion: a = false }: VisTypeAlias, { promotion: b = false }: VisTypeAlias) => - a === b ? 0 : a ? -1 : 1 - ); - - const factories = embeddablesService - ? Array.from(embeddablesService.getEmbeddableFactories()).filter( - ({ type, isEditable, canCreateNew, isContainerType }) => - isEditable() && - !isContainerType && - canCreateNew() && - !['visualization', 'ml'].some((factoryType) => { - return type.includes(factoryType); - }) - ) - : []; - - const promotedVisTypes = getVisTypesByGroup(VisGroups.PROMOTED); - - return ( - - ); -}; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/index.ts deleted file mode 100644 index 0f903b1bbbe2e..0000000000000 --- a/x-pack/plugins/canvas/public/components/workpad_header/editor_menu/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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { EditorMenu } from './editor_menu'; -export { EditorMenu as EditorMenuComponent } from './editor_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx index 1cfab236d9a9c..8ac581b0866a4 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/element_menu.component.tsx @@ -12,11 +12,11 @@ import { EuiContextMenu, EuiIcon, EuiContextMenuPanelItemDescriptor } from '@ela import { i18n } from '@kbn/i18n'; import { PrimaryActionPopover } from '../../../../../../../src/plugins/presentation_util/public'; import { getId } from '../../../lib/get_id'; +import { ClosePopoverFn } from '../../popover'; import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib'; import { ElementSpec } from '../../../../types'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { AssetManager } from '../../asset_manager'; -import { ClosePopoverFn } from '../../popover'; import { SavedElementsModal } from '../../saved_elements_modal'; interface CategorizedElementLists { @@ -112,7 +112,7 @@ const categorizeElementsByType = (elements: ElementSpec[]): { [key: string]: Ele return categories; }; -export interface Props { +interface Props { /** * Dictionary of elements from elements registry */ @@ -120,7 +120,7 @@ export interface Props { /** * Handler for adding a selected element to the workpad */ - addElement: (element: Partial) => void; + addElement: (element: ElementSpec) => void; } export const ElementMenu: FunctionComponent = ({ elements, addElement }) => { diff --git a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts index 037bb84b0cdba..52c8daece7690 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts +++ b/x-pack/plugins/canvas/public/components/workpad_header/element_menu/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { ElementMenu } from './element_menu.component'; +export { ElementMenu } from './element_menu'; +export { ElementMenu as ElementMenuComponent } from './element_menu.component'; diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx index b84e4faf2925e..f031d7c263199 100644 --- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx +++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.component.tsx @@ -27,7 +27,6 @@ import { ElementMenu } from './element_menu'; import { ShareMenu } from './share_menu'; import { ViewMenu } from './view_menu'; import { LabsControl } from './labs_control'; -import { EditorMenu } from './editor_menu'; const strings = { getFullScreenButtonAriaLabel: () => @@ -161,22 +160,24 @@ export const WorkpadHeader: FC = ({ - {isWriteable && ( - - - {{ - primaryActionButton: , - quickButtonGroup: , - addFromLibraryButton: , - extraButtons: [], - }} - - - )} + {isWriteable && ( + + + {{ + primaryActionButton: ( + + ), + quickButtonGroup: , + addFromLibraryButton: , + }} + + + )} @@ -191,7 +192,6 @@ export const WorkpadHeader: FC = ({ - diff --git a/x-pack/plugins/canvas/public/plugin.tsx b/x-pack/plugins/canvas/public/plugin.tsx index 912055dd47a62..723d1afea2860 100644 --- a/x-pack/plugins/canvas/public/plugin.tsx +++ b/x-pack/plugins/canvas/public/plugin.tsx @@ -8,7 +8,6 @@ import { BehaviorSubject } from 'rxjs'; import type { SharePluginSetup } from 'src/plugins/share/public'; import { ChartsPluginSetup, ChartsPluginStart } from 'src/plugins/charts/public'; -import { VisualizationsStart } from 'src/plugins/visualizations/public'; import { ReportingStart } from '../../reporting/public'; import { CoreSetup, @@ -64,7 +63,6 @@ export interface CanvasStartDeps { charts: ChartsPluginStart; data: DataPublicPluginStart; presentationUtil: PresentationUtilPluginStart; - visualizations: VisualizationsStart; spaces?: SpacesPluginStart; } @@ -124,12 +122,7 @@ export class CanvasPlugin const { pluginServices } = await import('./services'); pluginServices.setRegistry( - pluginServiceRegistry.start({ - coreStart, - startPlugins, - appUpdater: this.appUpdater, - initContext: this.initContext, - }) + pluginServiceRegistry.start({ coreStart, startPlugins, initContext: this.initContext }) ); // Load application bundle diff --git a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts index bd9a4e7141c27..35e79b442a15d 100644 --- a/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts +++ b/x-pack/plugins/canvas/public/routes/workpad/hooks/use_workpad.ts @@ -53,24 +53,14 @@ export const useWorkpad = ( workpad.aliasId = aliasId; } - if (storedWorkpad.id !== workpadId || storedWorkpad.aliasId !== aliasId) { - dispatch(setAssets(assets)); - dispatch(setWorkpad(workpad, { loadPages })); - dispatch(setZoomScale(1)); - } + dispatch(setAssets(assets)); + dispatch(setWorkpad(workpad, { loadPages })); + dispatch(setZoomScale(1)); } catch (e) { setError(e as Error | string); } })(); - }, [ - workpadId, - dispatch, - setError, - loadPages, - workpadResolve, - storedWorkpad.id, - storedWorkpad.aliasId, - ]); + }, [workpadId, dispatch, setError, loadPages, workpadResolve]); useEffect(() => { (() => { diff --git a/x-pack/plugins/canvas/public/services/embeddables.ts b/x-pack/plugins/canvas/public/services/embeddables.ts index 26b150b7a5349..24d7a57e086f2 100644 --- a/x-pack/plugins/canvas/public/services/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/embeddables.ts @@ -5,12 +5,8 @@ * 2.0. */ -import { - EmbeddableFactory, - EmbeddableStateTransfer, -} from '../../../../../src/plugins/embeddable/public'; +import { EmbeddableFactory } from '../../../../../src/plugins/embeddable/public'; export interface CanvasEmbeddablesService { getEmbeddableFactories: () => IterableIterator; - getStateTransfer: () => EmbeddableStateTransfer; } diff --git a/x-pack/plugins/canvas/public/services/index.ts b/x-pack/plugins/canvas/public/services/index.ts index ed55f919e4c76..f4292810b8089 100644 --- a/x-pack/plugins/canvas/public/services/index.ts +++ b/x-pack/plugins/canvas/public/services/index.ts @@ -17,7 +17,6 @@ import { CanvasNavLinkService } from './nav_link'; import { CanvasNotifyService } from './notify'; import { CanvasPlatformService } from './platform'; import { CanvasReportingService } from './reporting'; -import { CanvasVisualizationsService } from './visualizations'; import { CanvasWorkpadService } from './workpad'; export interface CanvasPluginServices { @@ -29,7 +28,6 @@ export interface CanvasPluginServices { notify: CanvasNotifyService; platform: CanvasPlatformService; reporting: CanvasReportingService; - visualizations: CanvasVisualizationsService; workpad: CanvasWorkpadService; } @@ -46,6 +44,4 @@ export const useNavLinkService = () => (() => pluginServices.getHooks().navLink. export const useNotifyService = () => (() => pluginServices.getHooks().notify.useService())(); export const usePlatformService = () => (() => pluginServices.getHooks().platform.useService())(); export const useReportingService = () => (() => pluginServices.getHooks().reporting.useService())(); -export const useVisualizationsService = () => - (() => pluginServices.getHooks().visualizations.useService())(); export const useWorkpadService = () => (() => pluginServices.getHooks().workpad.useService())(); diff --git a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts index 8d1a86edab3d8..054b9da7409fb 100644 --- a/x-pack/plugins/canvas/public/services/kibana/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/kibana/embeddables.ts @@ -16,5 +16,4 @@ export type EmbeddablesServiceFactory = KibanaPluginServiceFactory< export const embeddablesServiceFactory: EmbeddablesServiceFactory = ({ startPlugins }) => ({ getEmbeddableFactories: startPlugins.embeddable.getEmbeddableFactories, - getStateTransfer: startPlugins.embeddable.getStateTransfer, }); diff --git a/x-pack/plugins/canvas/public/services/kibana/index.ts b/x-pack/plugins/canvas/public/services/kibana/index.ts index 91767947bc0a6..1eb010e8d6f9d 100644 --- a/x-pack/plugins/canvas/public/services/kibana/index.ts +++ b/x-pack/plugins/canvas/public/services/kibana/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { labsServiceFactory } from './labs'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders< @@ -47,7 +45,6 @@ export const pluginServiceProviders: PluginServiceProviders< notify: new PluginServiceProvider(notifyServiceFactory), platform: new PluginServiceProvider(platformServiceFactory), reporting: new PluginServiceProvider(reportingServiceFactory), - visualizations: new PluginServiceProvider(visualizationsServiceFactory), workpad: new PluginServiceProvider(workpadServiceFactory), }; diff --git a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts b/x-pack/plugins/canvas/public/services/kibana/visualizations.ts deleted file mode 100644 index e319ec1c1f427..0000000000000 --- a/x-pack/plugins/canvas/public/services/kibana/visualizations.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { KibanaPluginServiceFactory } from '../../../../../../src/plugins/presentation_util/public'; -import { CanvasStartDeps } from '../../plugin'; -import { CanvasVisualizationsService } from '../visualizations'; - -export type VisualizationsServiceFactory = KibanaPluginServiceFactory< - CanvasVisualizationsService, - CanvasStartDeps ->; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = ({ startPlugins }) => ({ - showNewVisModal: startPlugins.visualizations.showNewVisModal, - getByGroup: startPlugins.visualizations.getByGroup, - getAliases: startPlugins.visualizations.getAliases, -}); diff --git a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts index 9c2cf4d0650ab..173d27563e2b2 100644 --- a/x-pack/plugins/canvas/public/services/stubs/embeddables.ts +++ b/x-pack/plugins/canvas/public/services/stubs/embeddables.ts @@ -14,5 +14,4 @@ const noop = (..._args: any[]): any => {}; export const embeddablesServiceFactory: EmbeddablesServiceFactory = () => ({ getEmbeddableFactories: noop, - getStateTransfer: noop, }); diff --git a/x-pack/plugins/canvas/public/services/stubs/index.ts b/x-pack/plugins/canvas/public/services/stubs/index.ts index 2216013a29c12..06a5ff49e9c04 100644 --- a/x-pack/plugins/canvas/public/services/stubs/index.ts +++ b/x-pack/plugins/canvas/public/services/stubs/index.ts @@ -22,7 +22,6 @@ import { navLinkServiceFactory } from './nav_link'; import { notifyServiceFactory } from './notify'; import { platformServiceFactory } from './platform'; import { reportingServiceFactory } from './reporting'; -import { visualizationsServiceFactory } from './visualizations'; import { workpadServiceFactory } from './workpad'; export { customElementServiceFactory } from './custom_element'; @@ -32,7 +31,6 @@ export { navLinkServiceFactory } from './nav_link'; export { notifyServiceFactory } from './notify'; export { platformServiceFactory } from './platform'; export { reportingServiceFactory } from './reporting'; -export { visualizationsServiceFactory } from './visualizations'; export { workpadServiceFactory } from './workpad'; export const pluginServiceProviders: PluginServiceProviders = { @@ -44,7 +42,6 @@ export const pluginServiceProviders: PluginServiceProviders; - -const noop = (..._args: any[]): any => {}; - -export const visualizationsServiceFactory: VisualizationsServiceFactory = () => ({ - showNewVisModal: noop, - getByGroup: noop, - getAliases: noop, -}); diff --git a/x-pack/plugins/canvas/public/services/visualizations.ts b/x-pack/plugins/canvas/public/services/visualizations.ts deleted file mode 100644 index c602b1dd39f3d..0000000000000 --- a/x-pack/plugins/canvas/public/services/visualizations.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { VisualizationsStart } from '../../../../../src/plugins/visualizations/public'; - -export interface CanvasVisualizationsService { - showNewVisModal: VisualizationsStart['showNewVisModal']; - getByGroup: VisualizationsStart['getByGroup']; - getAliases: VisualizationsStart['getAliases']; -} diff --git a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts index 092d4300d86b7..4cfdc7f21945f 100644 --- a/x-pack/plugins/canvas/public/state/reducers/embeddable.ts +++ b/x-pack/plugins/canvas/public/state/reducers/embeddable.ts @@ -40,7 +40,7 @@ export const embeddableReducer = handleActions< const element = pageWithElement.elements.find((elem) => elem.id === elementId); - if (!element || element.expression === embeddableExpression) { + if (!element) { return workpadState; } diff --git a/x-pack/plugins/canvas/server/plugin.ts b/x-pack/plugins/canvas/server/plugin.ts index ebe43ba76a46a..4071b891e4c3d 100644 --- a/x-pack/plugins/canvas/server/plugin.ts +++ b/x-pack/plugins/canvas/server/plugin.ts @@ -14,7 +14,6 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { BfetchServerSetup } from 'src/plugins/bfetch/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { HomeServerPluginSetup } from 'src/plugins/home/server'; -import { EmbeddableSetup } from 'src/plugins/embeddable/server'; import { ESSQL_SEARCH_STRATEGY } from '../common/lib/constants'; import { ReportingSetup } from '../../reporting/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; @@ -31,7 +30,6 @@ import { CanvasRouteHandlerContext, createWorkpadRouteContext } from './workpad_ interface PluginsSetup { expressions: ExpressionsServerSetup; - embeddable: EmbeddableSetup; features: FeaturesPluginSetup; home: HomeServerPluginSetup; bfetch: BfetchServerSetup; @@ -84,12 +82,7 @@ export class CanvasPlugin implements Plugin { const kibanaIndex = coreSetup.savedObjects.getKibanaIndex(); registerCanvasUsageCollector(plugins.usageCollection, kibanaIndex); - setupInterpreter(expressionsFork, { - embeddablePersistableStateService: { - extract: plugins.embeddable.extract, - inject: plugins.embeddable.inject, - }, - }); + setupInterpreter(expressionsFork); coreSetup.getStartServices().then(([_, depsStart]) => { const strategy = essqlSearchStrategyProvider(); diff --git a/x-pack/plugins/canvas/server/setup_interpreter.ts b/x-pack/plugins/canvas/server/setup_interpreter.ts index 849ad79717056..2fe23eb86c086 100644 --- a/x-pack/plugins/canvas/server/setup_interpreter.ts +++ b/x-pack/plugins/canvas/server/setup_interpreter.ts @@ -7,15 +7,9 @@ import { ExpressionsServerSetup } from 'src/plugins/expressions/server'; import { functions } from '../canvas_plugin_src/functions/server'; -import { - initFunctions as initExternalFunctions, - InitializeArguments, -} from '../canvas_plugin_src/functions/external'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; -export function setupInterpreter( - expressions: ExpressionsServerSetup, - dependencies: InitializeArguments -) { +export function setupInterpreter(expressions: ExpressionsServerSetup) { functions.forEach((f) => expressions.registerFunction(f)); - initExternalFunctions(dependencies).forEach((f) => expressions.registerFunction(f)); + externalFunctions.forEach((f) => expressions.registerFunction(f)); } diff --git a/x-pack/plugins/canvas/types/embeddables.ts b/x-pack/plugins/canvas/types/embeddables.ts deleted file mode 100644 index b78efece59d8f..0000000000000 --- a/x-pack/plugins/canvas/types/embeddables.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { TimeRange } from 'src/plugins/data/public'; -import { Filter } from '@kbn/es-query'; -import { EmbeddableInput as Input } from '../../../../src/plugins/embeddable/common/'; - -export type EmbeddableInput = Input & { - timeRange?: TimeRange; - filters?: Filter[]; - savedObjectId?: string; -}; diff --git a/x-pack/plugins/canvas/types/functions.ts b/x-pack/plugins/canvas/types/functions.ts index c80102915ed95..2569e0b10685b 100644 --- a/x-pack/plugins/canvas/types/functions.ts +++ b/x-pack/plugins/canvas/types/functions.ts @@ -10,8 +10,8 @@ import { UnwrapPromiseOrReturn } from '@kbn/utility-types'; import { functions as commonFunctions } from '../canvas_plugin_src/functions/common'; import { functions as browserFunctions } from '../canvas_plugin_src/functions/browser'; import { functions as serverFunctions } from '../canvas_plugin_src/functions/server'; -import { initFunctions as initExternalFunctions } from '../canvas_plugin_src/functions/external'; -import { initFunctions as initClientFunctions } from '../public/functions'; +import { functions as externalFunctions } from '../canvas_plugin_src/functions/external'; +import { initFunctions } from '../public/functions'; /** * A `ExpressionFunctionFactory` is a powerful type used for any function that produces @@ -90,11 +90,9 @@ export type FunctionFactory = type CommonFunction = FunctionFactory; type BrowserFunction = FunctionFactory; type ServerFunction = FunctionFactory; -type ExternalFunction = FunctionFactory< - ReturnType extends Array ? U : never ->; +type ExternalFunction = FunctionFactory; type ClientFunctions = FunctionFactory< - ReturnType extends Array ? U : never + ReturnType extends Array ? U : never >; /** diff --git a/x-pack/plugins/canvas/types/index.ts b/x-pack/plugins/canvas/types/index.ts index 930f337292088..09ae1510be6da 100644 --- a/x-pack/plugins/canvas/types/index.ts +++ b/x-pack/plugins/canvas/types/index.ts @@ -9,7 +9,6 @@ export * from '../../../../src/plugins/expressions/common'; export * from './assets'; export * from './canvas'; export * from './elements'; -export * from './embeddables'; export * from './filters'; export * from './functions'; export * from './renderers'; From cca0a082d2783413d686d4f23a6f60a6ffdd94e7 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 27 Oct 2021 21:07:08 -0500 Subject: [PATCH 17/22] e2e tests for dependencies (#116369) Also fix a link in testing readme. References #116290. --- x-pack/plugins/apm/ftr_e2e/README.md | 2 +- .../read_only_user/dependencies.spec.ts | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts diff --git a/x-pack/plugins/apm/ftr_e2e/README.md b/x-pack/plugins/apm/ftr_e2e/README.md index 2df4e837d2e55..96d6671bb3699 100644 --- a/x-pack/plugins/apm/ftr_e2e/README.md +++ b/x-pack/plugins/apm/ftr_e2e/README.md @@ -4,4 +4,4 @@ APM uses [FTR](../../../../packages/kbn-test/README.md) (functional test runner) ## Running tests -Go to [tests documentation](../scripts/test#e2e-tests-cypress/README.md) \ No newline at end of file +Go to [tests documentation](../dev_docs/testing.md#e2e-tests-cypress) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts new file mode 100644 index 0000000000000..0ab2d5682a900 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/dependencies.spec.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +const timeRange = { + rangeFrom: Cypress.env('START_DATE'), + rangeTo: Cypress.env('END_DATE'), +}; + +describe('Dependencies', () => { + beforeEach(() => { + cy.loginAsReadOnlyUser(); + }); + + describe('top-level dependencies page', () => { + it('has a list of dependencies and you can navigate to the page for one', () => { + cy.visit(`/app/apm/services?${new URLSearchParams(timeRange)}`); + cy.contains('nav a', 'Dependencies').click(); + + // `force: true` because Cypress says the element is 0x0 + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('dependency overview page', () => { + it('shows dependency information and you can navigate to a page for an upstream service', () => { + cy.visit( + `/app/apm/backends/overview?${new URLSearchParams({ + ...timeRange, + backendName: 'postgresql', + })}` + ); + + cy.get('[data-test-subj="latencyChart"]'); + cy.get('[data-test-subj="throughputChart"]'); + cy.get('[data-test-subj="errorRateChart"]'); + + cy.contains('opbeans-python').click({ force: true }); + + cy.contains('h1', 'opbeans-python'); + }); + }); + + describe('service overview page', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); + + describe('service dependencies tab', () => { + it('shows dependency information and you can navigate to a page for a dependency', () => { + cy.visit( + `/app/apm/services/opbeans-python/overview?${new URLSearchParams( + timeRange + )}` + ); + + cy.contains('a[role="tab"]', 'Dependencies').click(); + + cy.contains('Time spent by dependency'); + + cy.contains('postgresql').click({ force: true }); + + cy.contains('h1', 'postgresql'); + }); + }); +}); From a78e50ca94523688484d0327f76a656bfb4c1078 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 28 Oct 2021 03:13:46 +0100 Subject: [PATCH 18/22] skip flaky suite (#116059) --- .../test_suites/saved_objects_management/hidden_types.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts index 8e7adb504ebee..b384c3fbbbb1e 100644 --- a/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts +++ b/test/plugin_functional/test_suites/saved_objects_management/hidden_types.ts @@ -68,7 +68,8 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide }); }); - describe('Delete modal', () => { + // FLAKY: https://github.com/elastic/kibana/issues/116059 + describe.skip('Delete modal', () => { it('should display a warning then trying to delete hidden saved objects', async () => { await PageObjects.savedObjects.clickCheckboxByTitle('A Pie'); await PageObjects.savedObjects.clickCheckboxByTitle('A Dashboard'); From 3424f94fb4421c050930c90c59260b070440c0c1 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Wed, 27 Oct 2021 21:31:18 -0600 Subject: [PATCH 19/22] Unskips tests and fixes a few of them. (#116469) ## Summary * For the test of `create_endpoint_exceptions.ts` code was changed to try to reduce/remove flake. I put the code back to where it was and with the sorting introduced it all should pass * Added `await waitForSignalsToBePresent(supertest, 1, [id]);` to areas of code that were missing. * The `.flat` should be an added layer of protection for flakes. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../tests/create_endpoint_exceptions.ts | 25 +++++++++---------- .../ip_array.ts | 1 + .../exception_operators_data_types/text.ts | 14 +++-------- .../text_array.ts | 2 ++ 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts index d5e623989b460..6c6fcc366782a 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_endpoint_exceptions.ts @@ -70,8 +70,7 @@ export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); - // Flaky - describe.skip('Rule exception operators for endpoints', () => { + describe('Rule exception operators for endpoints', () => { before(async () => { await esArchiver.load( 'x-pack/test/functional/es_archives/rule_exceptions/endpoint_without_host_type' @@ -113,7 +112,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -135,7 +134,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -174,7 +173,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -210,7 +209,7 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { name: 'Macos' }, }, { os: { name: 'Windows' }, @@ -336,7 +335,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -372,7 +371,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, @@ -501,10 +500,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -546,10 +545,10 @@ export default ({ getService }: FtrProviderContext) => { os: { name: 'Linux' }, }, { - os: { name: 'Windows' }, + os: { type: 'macos' }, }, { - os: { type: 'windows' }, + os: { name: 'Macos' }, }, { os: { type: 'windows' }, @@ -876,7 +875,7 @@ export default ({ getService }: FtrProviderContext) => { os: { type: 'linux' }, }, { - os: { type: 'windows' }, + os: { type: 'macos' }, }, { os: { type: 'windows' }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts index 89f7693e72358..efff288fddac2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/ip_array.ts @@ -508,6 +508,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const ips = signalsOpen.hits.hits.map((hit) => hit._source?.ip).sort(); expect(ips.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts index 9fd733789588f..ff2f680654047 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text.ts @@ -426,9 +426,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['word four']); }); - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('should filter 4 text if all are set as exceptions', async () => { + it('should filter 4 text if all are set as exceptions', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -448,9 +446,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"is not one of" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if it cannot find what it is excluding', async () => { + it('will return 0 results if it cannot find what it is excluding', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -489,9 +485,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('"exists" operator', () => { - // This test is unreliable due to a race condition... we don't know if the rule ran and - // generated 0 signals, or if the index hasn't refreshed yet. - it.skip('will return 0 results if matching against text', async () => { + it('will return 0 results if matching against text', async () => { const rule = getRuleForSignalTesting(['text']); const { id } = await createRuleWithExceptionEntries(supertest, rule, [ [ @@ -577,7 +571,7 @@ export default ({ getService }: FtrProviderContext) => { expect(hits).to.eql(['four', 'two']); }); - it.skip('will return 0 results if we have a list that includes all text', async () => { + it('will return 0 results if we have a list that includes all text', async () => { await importTextFile( supertest, 'text', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts index 3bcf8692d58f9..6d25124667132 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/exception_operators_data_types/text_array.ts @@ -337,6 +337,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); @@ -515,6 +516,7 @@ export default ({ getService }: FtrProviderContext) => { ], ]); await waitForRuleSuccessOrStatus(supertest, id); + await waitForSignalsToBePresent(supertest, 1, [id]); const signalsOpen = await getSignalsById(supertest, id); const hits = signalsOpen.hits.hits.map((hit) => hit._source?.text).sort(); expect(hits.flat(Number.MAX_SAFE_INTEGER)).to.eql([]); From 2b15dc9b10a370a3d65b039c3d78eedfbb8a2bec Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 28 Oct 2021 09:36:52 +0200 Subject: [PATCH 20/22] retry reading suggestion list (#116405) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/visualize/_timelion.ts | 3 +-- test/functional/page_objects/timelion_page.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/test/functional/apps/visualize/_timelion.ts b/test/functional/apps/visualize/_timelion.ts index 712edb440311f..631d2148d73c3 100644 --- a/test/functional/apps/visualize/_timelion.ts +++ b/test/functional/apps/visualize/_timelion.ts @@ -257,8 +257,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { expect(value).to.eql('.es()'); }); - // FLAKY: https://github.com/elastic/kibana/issues/116033 - describe.skip('dynamic suggestions for argument values', () => { + describe('dynamic suggestions for argument values', () => { describe('.es()', () => { it('should show index pattern suggestions for index argument', async () => { await monacoEditor.setCodeEditorValue(''); diff --git a/test/functional/page_objects/timelion_page.ts b/test/functional/page_objects/timelion_page.ts index bdfde3c8145e5..ba1db60bc6350 100644 --- a/test/functional/page_objects/timelion_page.ts +++ b/test/functional/page_objects/timelion_page.ts @@ -7,13 +7,21 @@ */ import { FtrService } from '../ftr_provider_context'; +import type { WebElementWrapper } from '../services/lib/web_element_wrapper'; export class TimelionPageObject extends FtrService { private readonly testSubjects = this.ctx.getService('testSubjects'); + private readonly retry = this.ctx.getService('retry'); public async getSuggestionItemsText() { - const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); - const lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + let lists: WebElementWrapper[] = []; + await this.retry.try(async () => { + const timelionCodeEditor = await this.testSubjects.find('timelionCodeEditor'); + lists = await timelionCodeEditor.findAllByClassName('monaco-list-row'); + if (lists.length === 0) { + throw new Error('suggestion list not populated'); + } + }); return await Promise.all(lists.map(async (element) => await element.getVisibleText())); } From 01503c5d5d52e8db502dbe15e784a340ea91b713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Thu, 28 Oct 2021 10:14:40 +0200 Subject: [PATCH 21/22] Add stale bot for APM issues (#116432) * Add stale bot for APM issues * Update stale.yml * Update stale.yml --- .github/stale.yml | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000000..31bd822640359 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,58 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 180 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: false + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: ["Team:apm"] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: ["technical debt", "prevent stale"] + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed From 2e4c87211cd9b6b2ab9fd11fac19ecd5c34fcb09 Mon Sep 17 00:00:00 2001 From: Rudolf Meijering Date: Thu, 28 Oct 2021 10:36:57 +0200 Subject: [PATCH 22/22] [docs] Fix anchor link to saved objects migrations testing (#116427) --- dev_docs/tutorials/saved_objects.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev_docs/tutorials/saved_objects.mdx b/dev_docs/tutorials/saved_objects.mdx index 29a0b60983d90..9583e195d1c82 100644 --- a/dev_docs/tutorials/saved_objects.mdx +++ b/dev_docs/tutorials/saved_objects.mdx @@ -254,4 +254,4 @@ the error should be verbose and informative so that the corrupt document can be ### Testing Migrations -Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the . +Bugs in a migration function cause downtime for our users and therefore have a very high impact. Follow the .