From d9014671f4facf5753f5f9727b40a5608dd84576 Mon Sep 17 00:00:00 2001 From: "Quynh Nguyen (Quinn)" <43350163+qn895@users.noreply.github.com> Date: Sun, 26 May 2024 21:32:40 -0500 Subject: [PATCH 01/59] [ML][Embeddables Rebuild] Migrate Anomaly Chart (#183456) --- .../explorer/anomaly_context_menu.tsx | 10 +- .../explorer/explorer_charts/constants.ts | 1 + .../explorer_anomalies_container.tsx | 7 +- .../explorer_chart_distribution.js | 106 +++++----- .../explorer_chart_distribution.test.js | 5 +- .../explorer_chart_single_metric.js | 83 ++++---- .../explorer_chart_single_metric.test.js | 5 +- .../explorer_charts_container.js | 70 ++++--- .../draw_anomaly_explorer_charts_cursor.ts | 67 ++++++ .../cases/anomaly_charts_attachments.tsx | 173 +++++++++++---- .../register_anomaly_charts_attachment.tsx | 14 +- ...ble_anomaly_charts_container.test.tsx.snap | 64 ------ .../anomaly_charts_embeddable.tsx | 143 ------------- .../anomaly_charts_embeddable_factory.test.ts | 56 ----- .../anomaly_charts_embeddable_factory.ts | 123 ----------- .../anomaly_charts_embeddable_factory.tsx | 197 ++++++++++++++++++ .../anomaly_charts_initializer.test.tsx | 57 +++-- .../anomaly_charts_initializer.tsx | 154 +++++++++----- ...tsx => anomaly_charts_react_container.tsx} | 90 ++++---- .../anomaly_charts_setup_flyout.tsx | 87 ++++---- ...beddable_anomaly_charts_container.test.tsx | 189 ----------------- ...et_anomaly_charts_services_dependencies.ts | 64 ++++++ .../embeddables/anomaly_charts/index.ts | 2 +- .../initialize_anomaly_charts_controls.ts | 100 +++++++++ ...y.tsx => lazy_anomaly_charts_container.ts} | 7 +- .../embeddables/anomaly_charts/types.ts | 20 -- .../anomaly_charts/use_anomaly_charts_data.ts | 170 +++++++++++++++ .../use_anomaly_charts_input_resolver.test.ts | 197 ------------------ .../use_anomaly_charts_input_resolver.ts | 161 -------------- .../embeddables/anomaly_charts/utils.ts | 14 ++ .../anomaly_swimlane_embeddable_factory.tsx | 1 - .../anomaly_swimlane_initializer.tsx | 11 +- .../initialize_swim_lane_data_fetcher.ts | 10 +- .../embeddables/common/get_jobs_observable.ts | 15 +- .../embeddables/common/process_filters.ts | 3 +- .../common/resolve_job_selection.tsx | 107 ---------- .../embeddables/get_embeddable_component.tsx | 56 ----- x-pack/plugins/ml/public/embeddables/index.ts | 17 +- x-pack/plugins/ml/public/embeddables/types.ts | 89 ++++++-- x-pack/plugins/ml/public/embeddables/utils.ts | 17 ++ .../ui_actions/create_anomaly_chart.tsx | 82 ++++++++ .../edit_anomaly_charts_panel_action.tsx | 59 ------ x-pack/plugins/ml/public/ui_actions/index.ts | 9 +- .../open_in_anomaly_explorer_action.tsx | 16 +- .../models/results_service/results_service.ts | 2 +- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../group3/ml_embeddables_in_dashboard.ts | 9 +- .../anomaly_charts_dashboard_embeddables.ts | 8 +- .../anomaly_explorer.ts | 3 +- x-pack/test/functional/services/ml/cases.ts | 3 +- .../services/ml/dashboard_embeddables.ts | 14 +- 53 files changed, 1346 insertions(+), 1627 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx rename x-pack/plugins/ml/public/embeddables/anomaly_charts/{embeddable_anomaly_charts_container.tsx => anomaly_charts_react_container.tsx} (76%) delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts rename x-pack/plugins/ml/public/embeddables/anomaly_charts/{embeddable_anomaly_charts_container_lazy.tsx => lazy_anomaly_charts_container.ts} (62%) delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts delete mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts create mode 100644 x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts delete mode 100644 x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx delete mode 100644 x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx create mode 100644 x-pack/plugins/ml/public/embeddables/utils.ts create mode 100644 x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx delete mode 100644 x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx diff --git a/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx b/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx index afab1ef4fcb6a..b27f8efe4fcc6 100644 --- a/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx +++ b/x-pack/plugins/ml/public/application/explorer/anomaly_context_menu.tsx @@ -41,17 +41,17 @@ import { import type { TimeRangeBounds } from '@kbn/ml-time-buckets'; import { useTableSeverity } from '../components/controls/select_severity'; import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import { getDefaultExplorerChartsPanelTitle } from '../../embeddables/anomaly_charts/anomaly_charts_embeddable'; import { MAX_ANOMALY_CHARTS_ALLOWED } from '../../embeddables/anomaly_charts/anomaly_charts_initializer'; import { useAnomalyExplorerContext } from './anomaly_explorer_context'; import { escapeKueryForEmbeddableFieldValuePair } from '../util/string_utils'; import { useCasesModal } from '../contexts/kibana/use_cases_modal'; import { DEFAULT_MAX_SERIES_TO_PLOT } from '../services/anomaly_explorer_charts_service'; -import type { AnomalyChartsEmbeddableInput } from '../../embeddables'; +import type { AnomalyChartsEmbeddableState } from '../../embeddables'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../../embeddables'; import { useMlKibana } from '../contexts/kibana'; import type { AppStateSelectedCells, ExplorerJob } from './explorer_utils'; import { getSelectionInfluencers, getSelectionTimeRange } from './explorer_utils'; +import { getDefaultExplorerChartsPanelTitle } from '../../embeddables/anomaly_charts/utils'; interface AnomalyContextMenuProps { selectedJobs: ExplorerJob[]; @@ -133,7 +133,9 @@ export const AnomalyContextMenu: FC = ({ }, [chartsData.seriesToPlot, globalTimeRange, selectedCells, bounds, interval]); const isMaxSeriesToPlotValid = - maxSeriesToPlot >= 1 && maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; + typeof maxSeriesToPlot === 'number' && + maxSeriesToPlot >= 1 && + maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; const jobIds = selectedJobs.map(({ id }) => id); @@ -180,7 +182,7 @@ export const AnomalyContextMenu: FC = ({ ({ dashboardId, newTitle, newDescription }) => { const stateTransfer = embeddable!.getStateTransfer(); - const embeddableInput: Partial = { + const embeddableInput: Partial = { ...getEmbeddableInput(), title: newTitle, description: newDescription, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts index 37ef9bbc72898..110c053d11a30 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/constants.ts @@ -6,3 +6,4 @@ */ export const TRANSPARENT_BACKGROUND = 'rgba(0, 0, 0, 0)'; +export const CHART_HEIGHT = 170; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx index 6eaf0fffbf82b..d1a43559cbc98 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_anomalies_container.tsx @@ -62,7 +62,9 @@ export const ExplorerAnomaliesContainer: FC = ( timeRange, }) => { return ( - <> + // TODO: Remove data-shared-item and data-rendering-count as part of https://github.com/elastic/kibana/issues/179376 + // These attributes are temporarily needed for reporting to not have any warning +
@@ -96,9 +98,10 @@ export const ExplorerAnomaliesContainer: FC = ( tooManyBucketsCalloutMsg, showSelectedInterval, chartsService, + id, }} /> )} - +
); }; diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index f1794e812cc7e..58052f5f35a65 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -32,13 +32,17 @@ import { numTicksForDateFormat, removeLabelOverlap, chartExtendedLimits, + LINE_CHART_ANOMALY_RADIUS, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; import { CHART_TYPE } from '../explorer_constants'; -import { TRANSPARENT_BACKGROUND } from './constants'; +import { CHART_HEIGHT, TRANSPARENT_BACKGROUND } from './constants'; +import { filter } from 'rxjs'; +import { drawCursor } from './utils/draw_anomaly_explorer_charts_cursor'; const CONTENT_WRAPPER_HEIGHT = 215; +const SCHEDULED_EVENT_MARKER_HEIGHT = 5; // If a rare/event-distribution chart has a cardinality of 10 or less, // then the chart will display the y axis labels for each lane of events. @@ -54,10 +58,32 @@ export class ExplorerChartDistribution extends React.Component { seriesConfig: PropTypes.object, severity: PropTypes.number, tooltipService: PropTypes.object.isRequired, + cursor$: PropTypes.object, }; + constructor(props) { + super(props); + this.chartScales = undefined; + this.cursorStateSubscription = undefined; + } componentDidMount() { this.renderChart(); + this.cursorStateSubscription = this.props.cursor$ + .pipe(filter((c) => c.isDateHistogram)) + .subscribe((cursor) => { + drawCursor( + cursor.cursor, + this.rootNode, + this.props.id, + this.props.seriesConfig, + this.chartScales, + this.props.chartTheme + ); + }); + } + + componentWillUnmount() { + this.cursorStateSubscription?.unsubscribe(); } componentDidUpdate() { @@ -71,8 +97,7 @@ export class ExplorerChartDistribution extends React.Component { timeBuckets, showSelectedInterval, onPointerUpdate, - chartTheme, - cursor, + id: chartId, } = this.props; const element = this.rootNode; @@ -90,10 +115,6 @@ export class ExplorerChartDistribution extends React.Component { ); let vizWidth = 0; - const chartHeight = 170; - const LINE_CHART_ANOMALY_RADIUS = 7; - const SCHEDULED_EVENT_MARKER_HEIGHT = 5; - const chartType = getChartType(config); // Left margin is adjusted later for longest y-axis label. @@ -122,11 +143,12 @@ export class ExplorerChartDistribution extends React.Component { chartElement.select('svg').remove(); const svgWidth = element.clientWidth; - const svgHeight = chartHeight + margin.top + margin.bottom; + const svgHeight = CHART_HEIGHT + margin.top + margin.bottom; const svg = chartElement .append('svg') .classed('ml-explorer-chart-svg', true) + .attr('id', 'ml-explorer-chart-svg' + chartId) .attr('width', svgWidth) .attr('height', svgHeight); @@ -168,7 +190,7 @@ export class ExplorerChartDistribution extends React.Component { lineChartYScale = d3.scale .linear() - .range([chartHeight, 0]) + .range([CHART_HEIGHT, 0]) .domain([yScaleDomainMin < 0 ? yScaleDomainMin : 0, yScaleDomainMax]) .nice(); } else if (chartType === CHART_TYPE.EVENT_DISTRIBUTION) { @@ -176,7 +198,7 @@ export class ExplorerChartDistribution extends React.Component { const rowMargin = 5; lineChartYScale = d3.scale .ordinal() - .rangePoints([rowMargin, chartHeight - rowMargin]) + .rangePoints([rowMargin, CHART_HEIGHT - rowMargin]) .domain(scaleCategories); } else { throw new Error(`chartType '${chartType}' not supported`); @@ -260,7 +282,7 @@ export class ExplorerChartDistribution extends React.Component { .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .style('stroke', '#cccccc') .style('fill', 'none') @@ -268,17 +290,17 @@ export class ExplorerChartDistribution extends React.Component { drawRareChartAxes(); drawRareChartHighlightedSpan(); - drawSyncedCursorLine(lineChartGroup); + drawCursorListener(lineChartGroup); drawRareChartDots(data, lineChartGroup, lineChartValuesLine); drawRareChartMarkers(data); } - function drawSyncedCursorLine(lineChartGroup) { + function drawCursorListener(lineChartGroup) { lineChartGroup .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .on('mouseout', function () { onPointerUpdate({ @@ -293,46 +315,19 @@ export class ExplorerChartDistribution extends React.Component { .on('mousemove', function () { const mouse = d3.mouse(this); - onPointerUpdate({ - chartId: 'ml-anomaly-chart-metric', - scale: 'time', - smHorizontalValue: null, - smVerticalValue: null, - type: 'Over', - unit: undefined, - x: moment(lineChartXScale.invert(mouse[0])).unix() * 1000, - }); + if (onPointerUpdate) { + onPointerUpdate({ + chartId: 'ml-anomaly-chart-metric', + scale: 'time', + smHorizontalValue: null, + smVerticalValue: null, + type: 'Over', + unit: undefined, + x: moment(lineChartXScale.invert(mouse[0])).unix() * 1000, + }); + } }) .style('fill', TRANSPARENT_BACKGROUND); - - const cursorData = - cursor && - cursor.type === 'Over' && - cursor.x >= config.plotEarliest && - cursor.x <= config.plotLatest - ? [cursor.x] - : []; - - const cursorMouseLine = lineChartGroup - .append('g') - .attr('class', 'ml-anomaly-chart-cursor') - .selectAll('.ml-anomaly-chart-cursor-line') - .data(cursorData); - - cursorMouseLine - .enter() - .append('path') - .attr('class', 'ml-anomaly-chart-cursor-line') - .attr('d', (ts) => { - const xPosition = lineChartXScale(ts); - return `M${xPosition},${chartHeight} ${xPosition},0`; - }) - // Use elastic chart's cursor line style if possible - .style('stroke', chartTheme.crosshair.line.stroke) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); - - cursorMouseLine.exit().remove(); } function drawRareChartAxes() { @@ -350,7 +345,7 @@ export class ExplorerChartDistribution extends React.Component { .axis() .scale(lineChartXScale) .orient('bottom') - .innerTickSize(-chartHeight) + .innerTickSize(-CHART_HEIGHT) .outerTickSize(0) .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); @@ -389,7 +384,7 @@ export class ExplorerChartDistribution extends React.Component { const gAxis = axes .append('g') .attr('class', 'x axis') - .attr('transform', 'translate(0,' + chartHeight + ')') + .attr('transform', 'translate(0,' + CHART_HEIGHT + ')') .call(xAxis); axes.append('g').attr('class', 'y axis').call(yAxis); @@ -450,7 +445,7 @@ export class ExplorerChartDistribution extends React.Component { .attr('rx', 3) .attr('ry', 3) .attr('width', rectWidth - 4) - .attr('height', chartHeight - 4); + .attr('height', CHART_HEIGHT - 4); } function drawRareChartMarkers(data) { @@ -635,6 +630,7 @@ export class ExplorerChartDistribution extends React.Component { y: LINE_CHART_ANOMALY_RADIUS * 2, }); } + this.chartScales = { lineChartXScale, margin }; } shouldComponentUpdate() { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js index ee0c2c41074e0..246e46c50e5f0 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js @@ -7,6 +7,7 @@ import { chartData as mockChartData } from './__mocks__/mock_chart_data_rare'; import seriesConfig from './__mocks__/mock_series_config_rare.json'; +import { BehaviorSubject } from 'rxjs'; import { mountWithIntl } from '@kbn/test-jest-helpers'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -20,9 +21,7 @@ const utilityProps = { timeBuckets: timeBucketsMock, chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), - cursor: { - x: 10432423, - }, + cursor$: new BehaviorSubject({ isDataHistorgram: true, cursor: { x: 10432423 } }), }; describe('ExplorerChart', () => { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index 3291307710cb2..77118b376e97a 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -39,14 +39,15 @@ import { getMultiBucketImpactTooltipValue, } from '../../util/chart_utils'; import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator'; -import { TRANSPARENT_BACKGROUND } from './constants'; +import { CHART_HEIGHT, TRANSPARENT_BACKGROUND } from './constants'; +import { filter } from 'rxjs'; +import { drawCursor } from './utils/draw_anomaly_explorer_charts_cursor'; const CONTENT_WRAPPER_HEIGHT = 215; const CONTENT_WRAPPER_CLASS = 'ml-explorer-chart-content-wrapper'; export class ExplorerChartSingleMetric extends React.Component { static contextType = context; - static propTypes = { tooManyBuckets: PropTypes.bool, seriesConfig: PropTypes.object, @@ -55,11 +56,33 @@ export class ExplorerChartSingleMetric extends React.Component { timeBuckets: PropTypes.object.isRequired, onPointerUpdate: PropTypes.func.isRequired, chartTheme: PropTypes.object.isRequired, - cursor: PropTypes.object, + cursor$: PropTypes.object, + id: PropTypes.string.isRequired, }; + constructor(props) { + super(props); + this.chartScales = undefined; + } componentDidMount() { this.renderChart(); + + this.cursorStateSubscription = this.props.cursor$ + .pipe(filter((c) => c.isDateHistogram)) + .subscribe((cursor) => { + drawCursor( + cursor.cursor, + this.rootNode, + this.props.id, + this.props.seriesConfig, + this.chartScales, + this.props.chartTheme + ); + }); + } + + componentWillUnmount() { + this.cursorStateSubscription?.unsubscribe(); } componentDidUpdate() { @@ -73,8 +96,7 @@ export class ExplorerChartSingleMetric extends React.Component { timeBuckets, showSelectedInterval, onPointerUpdate, - chartTheme, - cursor, + id: chartId, } = this.props; const element = this.rootNode; @@ -92,7 +114,6 @@ export class ExplorerChartSingleMetric extends React.Component { ); let vizWidth = 0; - const chartHeight = 170; // Left margin is adjusted later for longest y-axis label. const margin = { top: 10, right: 0, bottom: 30, left: 60 }; @@ -112,18 +133,19 @@ export class ExplorerChartSingleMetric extends React.Component { chartElement.select('svg').remove(); const svgWidth = element.clientWidth; - const svgHeight = chartHeight + margin.top + margin.bottom; + const svgHeight = CHART_HEIGHT + margin.top + margin.bottom; const svg = chartElement .append('svg') .classed('ml-explorer-chart-svg', true) + .attr('id', 'ml-explorer-chart-svg' + chartId) .attr('width', svgWidth) .attr('height', svgHeight); // Set the size of the left margin according to the width of the largest y axis tick label. lineChartYScale = d3.scale .linear() - .range([chartHeight, 0]) + .range([CHART_HEIGHT, 0]) .domain([chartLimits.min, chartLimits.max]) .nice(); @@ -188,7 +210,7 @@ export class ExplorerChartSingleMetric extends React.Component { .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .style('stroke', '#cccccc') .style('fill', 'none') @@ -196,18 +218,18 @@ export class ExplorerChartSingleMetric extends React.Component { drawLineChartAxes(); drawLineChartHighlightedSpan(); - drawSyncedCursorLine(lineChartGroup); + drawCursorListener(lineChartGroup); drawLineChartPaths(data); drawLineChartDots(data, lineChartGroup, lineChartValuesLine); drawLineChartMarkers(data); } - function drawSyncedCursorLine(lineChartGroup) { + function drawCursorListener(lineChartGroup) { lineChartGroup .append('rect') .attr('x', 0) .attr('y', 0) - .attr('height', chartHeight) + .attr('height', CHART_HEIGHT) .attr('width', vizWidth) .on('mouseout', function () { onPointerUpdate({ @@ -235,35 +257,6 @@ export class ExplorerChartSingleMetric extends React.Component { } }) .style('fill', TRANSPARENT_BACKGROUND); - - const cursorData = - cursor && - cursor.type === 'Over' && - cursor.x >= config.plotEarliest && - cursor.x <= config.plotLatest - ? [cursor.x] - : []; - - const cursorMouseLine = lineChartGroup - .append('g') - .attr('class', 'ml-anomaly-chart-cursor') - .selectAll('.ml-anomaly-chart-cursor-line') - .data(cursorData); - - cursorMouseLine - .enter() - .append('path') - .attr('class', 'ml-anomaly-chart-cursor-line') - .attr('d', (ts) => { - const xPosition = lineChartXScale(ts); - return `M${xPosition},${chartHeight} ${xPosition},0`; - }) - // Use elastic chart's cursor line style if possible - .style('stroke', chartTheme.crosshair.line.stroke) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); - - cursorMouseLine.exit().remove(); } function drawLineChartAxes() { @@ -281,7 +274,7 @@ export class ExplorerChartSingleMetric extends React.Component { .axis() .scale(lineChartXScale) .orient('bottom') - .innerTickSize(-chartHeight) + .innerTickSize(-CHART_HEIGHT) .outerTickSize(0) .tickPadding(10) .tickFormat((d) => moment(d).format(xAxisTickFormat)); @@ -320,7 +313,7 @@ export class ExplorerChartSingleMetric extends React.Component { const gAxis = axes .append('g') .attr('class', 'x axis') - .attr('transform', 'translate(0,' + chartHeight + ')') + .attr('transform', 'translate(0,' + CHART_HEIGHT + ')') .call(xAxis); axes.append('g').attr('class', 'y axis').call(yAxis); @@ -348,7 +341,7 @@ export class ExplorerChartSingleMetric extends React.Component { .attr('rx', 3) .attr('ry', 3) .attr('width', rectWidth - 4) - .attr('height', chartHeight - 4); + .attr('height', CHART_HEIGHT - 4); } function drawLineChartPaths(data) { @@ -583,6 +576,8 @@ export class ExplorerChartSingleMetric extends React.Component { y: LINE_CHART_ANOMALY_RADIUS * 2, }); } + + this.chartScales = { lineChartXScale, margin }; } shouldComponentUpdate() { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js index 063762972e268..70eb254f99072 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js @@ -15,14 +15,13 @@ import React from 'react'; import { ExplorerChartSingleMetric } from './explorer_chart_single_metric'; import { timeBucketsMock } from '../../util/__mocks__/time_buckets'; import { kibanaContextMock } from '../../contexts/kibana/__mocks__/kibana_context'; +import { BehaviorSubject } from 'rxjs'; const utilityProps = { timeBuckets: timeBucketsMock, chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), - cursor: { - x: 10432423, - }, + cursor$: new BehaviorSubject({ isDataHistorgram: true, cursor: { x: 10432423 } }), }; describe('ExplorerChart', () => { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index 9e84a1f546b04..702aeed891ebc 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -44,7 +44,6 @@ import { addItemToRecentlyAccessed } from '../../util/recently_accessed'; import { EmbeddedMapComponentWrapper } from './explorer_chart_embedded_map'; import { useActiveCursor } from '@kbn/charts-plugin/public'; import { BarSeries, Chart, Settings, LEGACY_LIGHT_THEME } from '@elastic/charts'; -import useObservable from 'react-use/lib/useObservable'; import { escapeKueryForFieldValuePair } from '../../util/string_utils'; const textTooManyBuckets = i18n.translate('xpack.ml.explorer.charts.tooManyBucketsDescription', { @@ -78,15 +77,15 @@ export function getEntitiesQuery(series) { // create a somewhat unique ID // from charts metadata for React's key attribute -function getChartId(series) { - const { jobId, detectorLabel, entityFields } = series; - const entities = entityFields.map((ef) => `${ef.fieldName}/${ef.fieldValue}`).join(','); - const id = `${jobId}_${detectorLabel}_${entities}`; +function getChartId(series, randomId) { + const { jobId, detectorLabel } = series; + const id = `${jobId}${detectorLabel}`.replace(/[^a-zA-Z]+/g, '') + randomId; return id; } // Wrapper for a single explorer chart function ExplorerChartContainer({ + id, series, severity, tooManyBuckets, @@ -194,8 +193,6 @@ function ExplorerChartContainer({ isDateHistogram: true, }); - const cursor = useObservable(chartsService.activeCursor.activeCursor$)?.cursor; - const addToRecentlyAccessed = useCallback(() => { if (recentlyAccessed) { addItemToRecentlyAccessed( @@ -333,6 +330,7 @@ function ExplorerChartContainer({ {(tooltipService) => ( )} @@ -352,6 +350,7 @@ function ExplorerChartContainer({ {(tooltipService) => ( )} @@ -373,6 +372,7 @@ function ExplorerChartContainer({ // Flex layout wrapper for all explorer charts export const ExplorerChartsContainerUI = ({ + id: uuid, chartsPerRow, seriesToPlot, severity, @@ -422,6 +422,7 @@ export const ExplorerChartsContainerUI = ({ const chartsColumns = chartsPerRow === 1 ? 0 : chartsPerRow; const wrapLabel = seriesToUse.some((series) => isLabelLengthAboveThreshold(series)); + return ( <> @@ -431,29 +432,34 @@ export const ExplorerChartsContainerUI = ({ data-test-subj="mlExplorerChartsContainer" > {seriesToUse.length > 0 && - seriesToUse.map((series) => ( - - - - ))} + seriesToUse.map((series, idx) => { + const chartId = getChartId(series, '-' + (uuid ?? '') + idx); + return ( + + + + ); + })} ); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts b/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts new file mode 100644 index 0000000000000..dae80948e16e7 --- /dev/null +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/utils/draw_anomaly_explorer_charts_cursor.ts @@ -0,0 +1,67 @@ +/* + * 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 d3 from 'd3'; +import type { PartialTheme } from '@elastic/charts'; +import type { PointerEvent } from '@elastic/charts'; +import { CHART_HEIGHT } from '../constants'; +interface ChartScales { + lineChartXScale: (value: number | null | string) => number; + margin: { left: number; right: number; top: number; bottom: number }; +} +export function drawCursor( + cursor: Required, + rootNode: HTMLDivElement, + chartId: string, + config: { plotEarliest: number; plotLatest: number }, + chartScales: ChartScales, + chartTheme: PartialTheme +) { + if (!chartScales) return; + const { lineChartXScale, margin: updatedMargin } = chartScales; + + const element = rootNode; + const chartElement = d3.select(element).select('#ml-explorer-chart-svg' + chartId); + if (!chartElement || !lineChartXScale) return; + chartElement.select('.ml-anomaly-chart-cursor-line').remove(); + + const crosshairLine = chartTheme?.crosshair?.line ?? { + visible: true, + stroke: '#69707D', + strokeWidth: 1, + dash: [4, 4], + }; + const cursorData = + cursor && + cursor.type === 'Over' && + cursor.x !== null && + cursor.x >= config.plotEarliest && + cursor.x <= config.plotLatest + ? [cursor.x] + : []; + + const cursorMouseLine = chartElement + .append('g') + .attr('class', 'ml-anomaly-chart-cursor') + .selectAll('.ml-anomaly-chart-cursor-line') + .data(cursorData); + + // @ts-expect-error d3 types are not up to date + cursorMouseLine + .enter() + .append('path') + .attr('class', 'ml-anomaly-chart-cursor-line') + .attr('d', (ts) => { + const xPosition = lineChartXScale(ts); + return `M${xPosition},${CHART_HEIGHT} ${xPosition},0`; + }) + // Use elastic chart's cursor line style if possible + .style('stroke', crosshairLine.stroke) + .style('stroke-width', `${crosshairLine.strokeWidth}px`) + .style('stroke-dasharray', crosshairLine.dash?.join(',') ?? '4,4') + .attr('transform', 'translate(' + updatedMargin.left + ',' + updatedMargin.top + ')'); + cursorMouseLine.exit().remove(); +} diff --git a/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx b/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx index 8b4ee9eb77b76..71b854100bd4d 100644 --- a/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx +++ b/x-pack/plugins/ml/public/cases/anomaly_charts_attachments.tsx @@ -5,20 +5,94 @@ * 2.0. */ -import type { FC } from 'react'; -import React from 'react'; +import React, { useMemo } from 'react'; import { memoize } from 'lodash'; import deepEqual from 'fast-deep-equal'; - +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { BehaviorSubject } from 'rxjs'; +import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import type { PersistableStateAttachmentViewProps } from '@kbn/cases-plugin/public/client/attachment_framework/types'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; -import { EuiDescriptionList } from '@elastic/eui'; +import { EuiDescriptionList, htmlIdGenerator } from '@elastic/eui'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '../embeddables'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { LazyAnomalyChartsContainer } from '../embeddables/anomaly_charts/lazy_anomaly_charts_container'; +import { initializeAnomalyChartsControls } from '../embeddables/anomaly_charts/initialize_anomaly_charts_controls'; +import type { + AnomalyChartsEmbeddableServices, + AnomalyChartsAttachmentState, + AnomalyChartsAttachmentApi, +} from '../embeddables'; + +interface AnomalyChartsCaseAttachmentProps extends AnomalyChartsAttachmentState { + services: AnomalyChartsEmbeddableServices; +} +const AnomalyChartsCaseAttachment = ({ + services, + ...rawState +}: AnomalyChartsCaseAttachmentProps) => { + const id = useMemo(() => htmlIdGenerator()(), []); + const [coreStartServices, pluginStartServices, mlServices] = services; + const contextServices = useMemo( + () => ({ + mlServices: { + ...mlServices, + }, + ...pluginStartServices, + ...coreStartServices, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const api = useMemo(() => { + const initialState: AnomalyChartsAttachmentState = rawState ?? {}; + const filters$ = new BehaviorSubject(initialState.filters ?? []); + const query$ = new BehaviorSubject(initialState.query ?? undefined); + const timeRange$ = new BehaviorSubject(initialState.timeRange); -export const initComponent = memoize( - (fieldFormats: FieldFormatsStart, EmbeddableComponent: FC) => { + const anomalyChartsApi = initializeAnomalyChartsControls(initialState); + const combined: AnomalyChartsAttachmentApi = { + ...anomalyChartsApi.anomalyChartsControlsApi, + ...anomalyChartsApi.dataLoadingApi, + parentApi: { filters$, query$, timeRange$ }, + }; + return combined; + // Initialize services upon first mount already, + // as state management for cases + // already handled by the initial state + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ + + + + +
+ ); +}; + +function isValidTimeRange(arg: unknown): arg is TimeRange { + return isPopulatedObject(arg, ['from', 'to']); +} + +export const initializeAnomalyChartsAttachment = memoize( + (fieldFormats: FieldFormatsStart, services: AnomalyChartsEmbeddableServices) => { return React.memo( (props: PersistableStateAttachmentViewProps) => { const { persistableStateAttachmentState } = props; @@ -28,46 +102,59 @@ export const initComponent = memoize( }); const inputProps = - persistableStateAttachmentState as unknown as AnomalyChartsEmbeddableInput; + persistableStateAttachmentState as unknown as AnomalyChartsAttachmentState; + + const descriptions = useMemo(() => { + const listItems = [ + { + title: ( + + ), + description: inputProps.jobIds.join(', '), + }, + ]; - const listItems = [ - { - title: ( - - ), - description: inputProps.jobIds.join(', '), - }, - { - title: ( - - ), - description: `${dataFormatter.convert( - inputProps.timeRange.from - )} - ${dataFormatter.convert(inputProps.timeRange.to)}`, - }, - ]; + if (isValidTimeRange(inputProps.timeRange)) { + listItems.push({ + title: ( + + ), + description: `${dataFormatter.convert( + inputProps.timeRange.from + )} - ${dataFormatter.convert(inputProps.timeRange.to)}`, + }); + } - if (typeof inputProps.query?.query === 'string' && inputProps.query?.query !== '') { - listItems.push({ - title: ( - - ), - description: inputProps.query?.query, - }); - } + if (typeof inputProps.query?.query === 'string' && inputProps.query?.query !== '') { + listItems.push({ + title: ( + + ), + description: inputProps.query?.query, + }); + } + return listItems; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + dataFormatter, + inputProps.jobIds, + inputProps.query?.query, + inputProps.timeRange?.from, + inputProps.timeRange?.to, + ]); return ( <> - - + + ); }, diff --git a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx index bb22df1d19934..cc5b00a35b805 100644 --- a/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx +++ b/x-pack/plugins/ml/public/cases/register_anomaly_charts_attachment.tsx @@ -11,21 +11,15 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { CasesPublicSetup } from '@kbn/cases-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import { CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS } from '../../common/constants/cases'; -import { getEmbeddableComponent } from '../embeddables'; import type { MlStartDependencies } from '../plugin'; import { PLUGIN_ICON } from '../../common/constants/app'; +import { getAnomalyChartsServiceDependencies } from '../embeddables/anomaly_charts/get_anomaly_charts_services_dependencies'; export function registerAnomalyChartsCasesAttachment( cases: CasesPublicSetup, coreStart: CoreStart, pluginStart: MlStartDependencies ) { - const EmbeddableComponent = getEmbeddableComponent( - CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, - coreStart, - pluginStart - ); - cases.attachmentFramework.registerPersistableState({ id: CASE_ATTACHMENT_TYPE_ID_ANOMALY_EXPLORER_CHARTS, icon: PLUGIN_ICON, @@ -41,9 +35,11 @@ export function registerAnomalyChartsCasesAttachment( ), timelineAvatar: PLUGIN_ICON, children: React.lazy(async () => { - const { initComponent } = await import('./anomaly_charts_attachments'); + const { initializeAnomalyChartsAttachment } = await import('./anomaly_charts_attachments'); + const services = await getAnomalyChartsServiceDependencies(coreStart, pluginStart); + return { - default: initComponent(pluginStart.fieldFormats, EmbeddableComponent), + default: initializeAnomalyChartsAttachment(pluginStart.fieldFormats, services), }; }), }), diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap b/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap deleted file mode 100644 index f40d239b65be3..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/__snapshots__/embeddable_anomaly_charts_container.test.tsx.snap +++ /dev/null @@ -1,64 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`EmbeddableAnomalyChartsContainer should render explorer charts with a valid embeddable input 1`] = ` -Object { - "chartsData": Object { - "chartsPerRow": 2, - "errorMessages": undefined, - "seriesToPlot": Array [], - "timeFieldName": "@timestamp", - "tooManyBuckets": false, - }, - "chartsService": undefined, - "id": "test-explorer-charts-embeddable", - "mlLocator": undefined, - "onSelectEntity": [Function], - "setSeverity": [Function], - "severity": Object { - "color": "#fe5050", - "display": "critical", - "val": 75, - }, - "showCharts": true, - "showSelectedInterval": false, - "timeBuckets": TimeBuckets { - "_fieldFormats": undefined, - "_timeBucketsConfig": Object { - "dateFormat": undefined, - "dateFormat:scaled": undefined, - "histogram:barTarget": undefined, - "histogram:maxBars": undefined, - }, - "barTarget": undefined, - "maxBars": undefined, - }, - "timeRange": undefined, - "timefilter": Object { - "calculateBounds": [MockFunction], - "createFilter": [MockFunction], - "createRelativeFilter": [MockFunction], - "disableAutoRefreshSelector": [MockFunction], - "disableTimeRangeSelector": [MockFunction], - "enableAutoRefreshSelector": [MockFunction], - "enableTimeRangeSelector": [MockFunction], - "getAbsoluteTime": [MockFunction], - "getActiveBounds": [MockFunction], - "getAutoRefreshFetch$": [MockFunction], - "getBounds": [MockFunction], - "getEnabledUpdated$": [MockFunction], - "getFetch$": [MockFunction], - "getRefreshInterval": [MockFunction], - "getRefreshIntervalDefaults": [MockFunction], - "getRefreshIntervalUpdate$": [MockFunction], - "getTime": [MockFunction], - "getTimeDefaults": [MockFunction], - "getTimeUpdate$": [MockFunction], - "isAutoRefreshSelectorEnabled": [MockFunction], - "isRefreshIntervalTouched": [MockFunction], - "isTimeRangeSelectorEnabled": [MockFunction], - "isTimeTouched": [MockFunction], - "setRefreshInterval": [MockFunction], - "setTime": [MockFunction], - }, -} -`; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx deleted file mode 100644 index 9d7b08fa935d0..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx +++ /dev/null @@ -1,143 +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, { Suspense } from 'react'; -import ReactDOM from 'react-dom'; -import type { CoreStart } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import { Subject, Subscription, type BehaviorSubject } from 'rxjs'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; -import type { IContainer } from '@kbn/embeddable-plugin/public'; -import { embeddableInputToSubject } from '@kbn/embeddable-plugin/public'; -import { embeddableOutputToSubject } from '@kbn/embeddable-plugin/public'; -import type { MlEntityField } from '@kbn/ml-anomaly-utils'; -import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container_lazy'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import type { MlDependencies } from '../../application/app'; -import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, - AnomalyChartsServices, -} from '..'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; -import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback'; -import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable'; - -export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) => - i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', { - defaultMessage: 'ML anomaly charts for {jobIds}', - values: { jobIds: jobIds.join(', ') }, - }); - -export type IAnomalyChartsEmbeddable = typeof AnomalyChartsEmbeddable; - -export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable< - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput -> { - private node?: HTMLElement; - private reload$ = new Subject(); - public readonly type: string = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE; - - // API - public readonly jobIds: BehaviorSubject; - public entityFields: BehaviorSubject; - - private apiSubscriptions = new Subscription(); - - constructor( - initialInput: AnomalyChartsEmbeddableInput, - public services: [CoreStart, MlDependencies, AnomalyChartsServices], - parent?: IContainer - ) { - super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); - - this.jobIds = embeddableInputToSubject( - this.apiSubscriptions, - this, - 'jobIds' - ); - - this.entityFields = embeddableOutputToSubject( - this.apiSubscriptions, - this, - 'entityFields' - ); - } - - public onLoading() { - this.renderComplete.dispatchInProgress(); - this.updateOutput({ loading: true, error: undefined }); - } - - public onError(error: Error) { - this.renderComplete.dispatchError(); - this.updateOutput({ loading: false, error: { name: error.name, message: error.message } }); - } - - public onRenderComplete() { - this.renderComplete.dispatchComplete(); - this.updateOutput({ loading: false, rendered: true, error: undefined }); - } - - public render(node: HTMLElement) { - super.render(node); - this.node = node; - - // required for the export feature to work - this.node.setAttribute('data-shared-item', ''); - - ReactDOM.render( - - - }> - - - - , - node - ); - } - - public destroy() { - super.destroy(); - - this.apiSubscriptions.unsubscribe(); - - if (this.node) { - ReactDOM.unmountComponentAtNode(this.node); - } - } - - public reload() { - this.reload$.next(); - } - - public supportedTriggers() { - return []; - } -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts deleted file mode 100644 index 4d82ca0b430c2..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { AnomalyChartsEmbeddableFactory } from './anomaly_charts_embeddable_factory'; -import { coreMock } from '@kbn/core/public/mocks'; -import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; -import { AnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; -import type { AnomalyChartsEmbeddableInput } from '..'; - -jest.mock('./anomaly_charts_embeddable', () => ({ - AnomalyChartsEmbeddable: jest.fn(), -})); - -describe('AnomalyChartsEmbeddableFactory', () => { - test('should provide required services on create', async () => { - // arrange - const pluginStartDeps = { data: dataPluginMock.createStartContract() }; - - const getStartServices = coreMock.createSetup({ - pluginStartDeps, - }).getStartServices; - - const [coreStart, pluginsStart] = await getStartServices(); - - // act - const factory = new AnomalyChartsEmbeddableFactory(getStartServices); - - await factory.create({ - jobIds: ['test-job'], - maxSeriesToPlot: 4, - } as AnomalyChartsEmbeddableInput); - - // assert - const mockCalls = (AnomalyChartsEmbeddable as unknown as jest.Mock) - .mock.calls[0]; - const input = mockCalls[0]; - const createServices = mockCalls[1]; - - expect(input).toEqual({ - jobIds: ['test-job'], - maxSeriesToPlot: 4, - }); - expect(Object.keys(createServices[0])).toEqual(Object.keys(coreStart)); - expect(createServices[1]).toMatchObject(pluginsStart); - expect(Object.keys(createServices[2])).toEqual([ - 'anomalyDetectorService', - 'anomalyExplorerService', - 'mlFieldFormatService', - 'mlResultsService', - ]); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts deleted file mode 100644 index 0ab25954ae7c0..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.ts +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; - -import type { StartServicesAccessor } from '@kbn/core/public'; - -import type { EmbeddableFactoryDefinition, IContainer } from '@kbn/embeddable-plugin/public'; -import { PLUGIN_ICON, PLUGIN_ID, ML_APP_NAME } from '../../../common/constants/app'; -import { HttpService } from '../../application/services/http_service'; -import type { MlPluginStart, MlStartDependencies } from '../../plugin'; -import type { MlDependencies } from '../../application/app'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsEmbeddableServices } from '..'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; -import { AnomalyExplorerChartsService } from '../../application/services/anomaly_explorer_charts_service'; - -export class AnomalyChartsEmbeddableFactory - implements EmbeddableFactoryDefinition -{ - public readonly type = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE; - - public readonly grouping = [ - { - id: PLUGIN_ID, - getDisplayName: () => ML_APP_NAME, - getIconType: () => PLUGIN_ICON, - }, - ]; - - constructor( - private getStartServices: StartServicesAccessor - ) {} - - public async isEditable() { - return true; - } - - public getDisplayName() { - return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', { - defaultMessage: 'Anomaly chart', - }); - } - - public getDescription() { - return i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.description', { - defaultMessage: 'View anomaly detection results in a chart.', - }); - } - - public async getExplicitInput(): Promise> { - const [coreStart, deps] = await this.getServices(); - - try { - const { resolveEmbeddableAnomalyChartsUserInput } = await import( - './anomaly_charts_setup_flyout' - ); - return await resolveEmbeddableAnomalyChartsUserInput(coreStart, deps.data.dataViews); - } catch (e) { - return Promise.reject(); - } - } - - private async getServices(): Promise { - const [ - [coreStart, pluginsStart], - { AnomalyDetectorService }, - { fieldFormatServiceFactory }, - { indexServiceFactory }, - { mlApiServicesProvider }, - { mlResultsServiceProvider }, - ] = await Promise.all([ - await this.getStartServices(), - await import('../../application/services/anomaly_detector_service'), - await import('../../application/services/field_format_service_factory'), - await import('../../application/util/index_service'), - await import('../../application/services/ml_api_service'), - await import('../../application/services/results_service'), - ]); - - const httpService = new HttpService(coreStart.http); - const anomalyDetectorService = new AnomalyDetectorService(httpService); - const mlApiServices = mlApiServicesProvider(httpService); - const mlResultsService = mlResultsServiceProvider(mlApiServices); - const anomalyExplorerService = new AnomalyExplorerChartsService( - pluginsStart.data.query.timefilter.timefilter, - mlApiServices, - mlResultsService - ); - - // Note on the following services: - // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`, - // but it's not being made available as part of global services. Since it's just - // some stateless utils `useMlIndexUtils()` should be used from within components. - // - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache", - // so because of its own state it needs to be made available as a global service. - // In the long run we should again try to get rid of it here and make it available via - // its own context or possibly without having a singleton like state at all, since the - // way this manages its own state right now doesn't consider React component lifecycles. - const mlIndexUtils = indexServiceFactory(pluginsStart.data.dataViews); - const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils); - - return [ - coreStart, - pluginsStart as MlDependencies, - { - anomalyDetectorService, - anomalyExplorerService, - mlFieldFormatService, - mlResultsService, - }, - ]; - } - - public async create(initialInput: AnomalyChartsEmbeddableInput, parent?: IContainer) { - const services = await this.getServices(); - const { AnomalyChartsEmbeddable } = await import('./anomaly_charts_embeddable'); - return new AnomalyChartsEmbeddable(initialInput, services, parent); - } -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx new file mode 100644 index 0000000000000..1dc792ffc44bf --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable_factory.tsx @@ -0,0 +1,197 @@ +/* + * 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 { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import React from 'react'; +import type { StartServicesAccessor } from '@kbn/core/public'; +import type { Observable } from 'rxjs'; +import { Subscription, map } from 'rxjs'; +import { fetch$ } from '@kbn/presentation-publishing'; +import useUnmount from 'react-use/lib/useUnmount'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { + apiHasExecutionContext, + initializeTimeRange, + initializeTitles, +} from '@kbn/presentation-publishing'; +import { distinctUntilChanged } from 'rxjs'; +import fastIsEqual from 'fast-deep-equal'; +import type { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import type { TimeRange } from '@kbn/es-query'; +import { css } from '@emotion/react'; +import { useEuiTheme } from '@elastic/eui'; +import type { MlPluginStart, MlStartDependencies } from '../../plugin'; +import type { AnomalyChartsEmbeddableApi, AnomalyChartsEmbeddableState } from '..'; +import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; +import { useReactEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; +import { initializeAnomalyChartsControls } from './initialize_anomaly_charts_controls'; +import { LazyAnomalyChartsContainer } from './lazy_anomaly_charts_container'; +import { getAnomalyChartsServiceDependencies } from './get_anomaly_charts_services_dependencies'; +import { buildDataViewPublishingApi } from '../common/anomaly_detection_embeddable'; + +export const getAnomalyChartsReactEmbeddableFactory = ( + getStartServices: StartServicesAccessor +) => { + const factory: ReactEmbeddableFactory = + { + type: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + deserializeState: (state) => state.rawState, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + if (!apiHasExecutionContext(parentApi)) { + throw new Error('Parent API does not have execution context'); + } + const [coreStartServices, pluginsStartServices] = await getStartServices(); + const anomalyChartsDependencies = await getAnomalyChartsServiceDependencies( + coreStartServices, + pluginsStartServices + ); + + const [, , mlServices] = anomalyChartsDependencies; + + const subscriptions = new Subscription(); + + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const { + api: timeRangeApi, + comparators: timeRangeComparators, + serialize: serializeTimeRange, + } = initializeTimeRange(state); + + const { + anomalyChartsControlsApi, + dataLoadingApi, + serializeAnomalyChartsState, + anomalyChartsComparators, + onAnomalyChartsDestroy, + } = initializeAnomalyChartsControls(state, titlesApi, parentApi); + + const api = buildApi( + { + isEditingEnabled: () => true, + getTypeDisplayName: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.typeDisplayName', { + defaultMessage: 'anomaly charts', + }), + onEdit: async () => { + try { + const { resolveEmbeddableAnomalyChartsUserInput } = await import( + './anomaly_charts_setup_flyout' + ); + const result = await resolveEmbeddableAnomalyChartsUserInput( + coreStartServices, + pluginsStartServices, + parentApi, + uuid, + { + ...serializeTitles(), + ...serializeAnomalyChartsState(), + } + ); + anomalyChartsControlsApi.updateUserInput(result); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return Promise.reject(); + } + }, + ...titlesApi, + ...timeRangeApi, + ...anomalyChartsControlsApi, + ...dataLoadingApi, + dataViews: buildDataViewPublishingApi( + { + anomalyDetectorService: mlServices.anomalyDetectorService, + dataViewsService: pluginsStartServices.data.dataViews, + }, + { jobIds: anomalyChartsControlsApi.jobIds$ }, + subscriptions + ), + serializeState: () => { + return { + rawState: { + timeRange: undefined, + ...serializeTitles(), + ...serializeTimeRange(), + ...serializeAnomalyChartsState(), + }, + references: [], + }; + }, + }, + { + ...timeRangeComparators, + ...titleComparators, + ...anomalyChartsComparators, + } + ); + + const appliedTimeRange$: Observable = fetch$(api).pipe( + map((fetchContext) => fetchContext.timeRange), + distinctUntilChanged(fastIsEqual) + ); + + const { onRenderComplete, onLoading, onError } = dataLoadingApi; + const contextServices = { + mlServices: { + ...mlServices, + }, + ...coreStartServices, + ...pluginsStartServices, + }; + + return { + api, + Component: () => { + if (!apiHasExecutionContext(parentApi)) { + throw new Error('Parent API does not have execution context'); + } + + useReactEmbeddableExecutionContext( + coreStartServices.executionContext, + parentApi.executionContext, + ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + uuid + ); + + useUnmount(() => { + onAnomalyChartsDestroy(); + subscriptions.unsubscribe(); + }); + const { euiTheme } = useEuiTheme(); + + return ( + + +
+ +
+
+
+ ); + }, + }; + }, + }; + return factory; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx index 9bfce47aa9dc1..4c68437933175 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.test.tsx @@ -5,45 +5,62 @@ * 2.0. */ -import { render, screen } from '@testing-library/react'; +import { render, screen, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import type { MlApiServices } from '../../application/services/ml_api_service'; import { AnomalyChartsInitializer } from './anomaly_charts_initializer'; import { I18nProvider } from '@kbn/i18n-react'; import React from 'react'; -import { getDefaultExplorerChartsPanelTitle } from './anomaly_charts_embeddable'; +import { getDefaultExplorerChartsPanelTitle } from './utils'; +import { kibanaContextMock } from '../../application/contexts/kibana/__mocks__/kibana_context'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public/context/context'; const defaultOptions = { wrapper: I18nProvider }; +jest.mock('../../application/services/anomaly_detector_service', () => { + return { + AnomalyDetectorService: jest.fn().mockImplementation(() => { + return { + getJobs$: jest.fn(), + }; + }), + }; +}); describe('AnomalyChartsInitializer', () => { test('should render anomaly charts initializer', async () => { const onCreate = jest.fn(); const onCancel = jest.fn(); + const adJobsApiService = jest.fn(); - const jobIds = ['test-job']; + const jobIds = ['job1', 'job2']; const defaultTitle = getDefaultExplorerChartsPanelTitle(jobIds); const input = { maxSeriesToPlot: 12, + jobIds, }; - const { getByTestId } = render( - onCreate(params)} - onCancel={onCancel} - />, + render( + + onCreate(params)} + onCancel={onCancel} + adJobsApiService={adJobsApiService as unknown as MlApiServices['jobs']} + /> + , defaultOptions ); - const confirmButton = screen.getByText(/Confirm/i).closest('button'); - expect(confirmButton).toBeDefined(); - expect(onCreate).toHaveBeenCalledTimes(0); - userEvent.click(confirmButton!); - expect(onCreate).toHaveBeenCalledWith({ - panelTitle: defaultTitle, - maxSeriesToPlot: input.maxSeriesToPlot, - }); + act(() => { + const confirmButton = screen.getByText(/Confirm/i).closest('button'); + expect(confirmButton).toBeDefined(); + expect(onCreate).toHaveBeenCalledTimes(0); - userEvent.clear(await getByTestId('panelTitleInput')); - expect(confirmButton).toHaveAttribute('disabled'); + userEvent.click(confirmButton!); + expect(onCreate).toHaveBeenCalledWith({ + jobIds: ['job1', 'job2'], + title: defaultTitle, + maxSeriesToPlot: input.maxSeriesToPlot, + }); + }); }); }); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx index add35ce0e19ce..536084cc01496 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_initializer.tsx @@ -6,64 +6,101 @@ */ import type { FC } from 'react'; -import React, { useState } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import { EuiButton, EuiButtonEmpty, EuiForm, EuiFormRow, - EuiModalBody, - EuiModalFooter, - EuiModalHeader, - EuiModalHeaderTitle, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, EuiFieldNumber, EuiFieldText, - EuiModal, + EuiSpacer, + EuiFlexGroup, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { AnomalyChartsEmbeddableInput } from '..'; +import type { AnomalyChartsEmbeddableState } from '..'; import { DEFAULT_MAX_SERIES_TO_PLOT } from '../../application/services/anomaly_explorer_charts_service'; +import { JobSelectorControl } from '../../alerting/job_selector'; +import { ML_PAGES } from '../../../common/constants/locator'; +import { getDefaultExplorerChartsPanelTitle } from './utils'; +import { useMlLink } from '../../application/contexts/kibana'; +import { getJobSelectionErrors } from '../utils'; +import type { MlApiServices } from '../../application/services/ml_api_service'; export const MAX_ANOMALY_CHARTS_ALLOWED = 50; export interface AnomalyChartsInitializerProps { - defaultTitle: string; - initialInput?: Partial>; - onCreate: (props: { panelTitle: string; maxSeriesToPlot?: number }) => void; + initialInput?: Partial< + Pick + >; + onCreate: (props: { + jobIds: AnomalyChartsEmbeddableState['jobIds']; + title: string; + maxSeriesToPlot?: number; + }) => void; onCancel: () => void; + adJobsApiService: MlApiServices['jobs']; } export const AnomalyChartsInitializer: FC = ({ - defaultTitle, initialInput, onCreate, onCancel, + adJobsApiService, }) => { - const [panelTitle, setPanelTitle] = useState(defaultTitle); + const titleManuallyChanged = useRef(!!initialInput?.title); + + const [panelTitle, setPanelTitle] = useState(initialInput?.title ?? ''); const [maxSeriesToPlot, setMaxSeriesToPlot] = useState( initialInput?.maxSeriesToPlot ?? DEFAULT_MAX_SERIES_TO_PLOT ); - - const isPanelTitleValid = panelTitle.length > 0; + const isPanelTitleValid = panelTitle?.length > 0; const isMaxSeriesToPlotValid = maxSeriesToPlot >= 1 && maxSeriesToPlot <= MAX_ANOMALY_CHARTS_ALLOWED; - const isFormValid = isPanelTitleValid && isMaxSeriesToPlotValid; + const newJobUrl = useMlLink({ page: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB }); + + const [jobIds, setJobIds] = useState(initialInput?.jobIds ?? []); + const jobIdsErrors = getJobSelectionErrors(jobIds); + + const isFormValid = isPanelTitleValid && isMaxSeriesToPlotValid && jobIdsErrors === undefined; + + useEffect( + function updateDefaultTitle() { + if (!titleManuallyChanged.current) { + setPanelTitle(getDefaultExplorerChartsPanelTitle(jobIds)); + } + }, + [initialInput?.title, jobIds] + ); return ( - - - - - - + <> + + +

+ +

+
+
- + + { + setJobIds([...(update?.jobIds ?? []), ...(update?.groupIds ?? [])]); + }} + errors={jobIdsErrors} + /> + = ({ id="panelTitle" name="panelTitle" value={panelTitle} - onChange={(e) => setPanelTitle(e.target.value)} + onChange={(e) => { + titleManuallyChanged.current = true; + setPanelTitle(e.target.value); + }} isInvalid={!isPanelTitleValid} /> @@ -112,31 +152,37 @@ export const AnomalyChartsInitializer: FC = ({ /> - + - - - - + + + + + - - - - -
+ + + + + + ); }; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx similarity index 76% rename from x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx rename to x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx index e7c5ce816b02b..d31ee17ef0780 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_react_container.tsx @@ -12,20 +12,19 @@ import type { Observable } from 'rxjs'; import { FormattedMessage } from '@kbn/i18n-react'; import { throttle } from 'lodash'; import { UI_SETTINGS } from '@kbn/data-plugin/common'; -import useObservable from 'react-use/lib/useObservable'; import { type MlEntityField, type MlEntityFieldOperation, ML_ANOMALY_THRESHOLD, } from '@kbn/ml-anomaly-utils'; import { TimeBuckets } from '@kbn/ml-time-buckets'; -import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { IAnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; +import useObservable from 'react-use/lib/useObservable'; +import type { TimeRange } from '@kbn/es-query'; import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, + AnomalyChartsEmbeddableOverridableState, AnomalyChartsEmbeddableServices, + AnomalyChartsApi, + AnomalyChartsAttachmentApi, } from '..'; import { ExplorerAnomaliesContainer } from '../../application/explorer/explorer_charts/explorer_anomalies_container'; @@ -33,52 +32,43 @@ import { ML_APP_LOCATOR } from '../../../common/constants/locator'; import { optionValueToThreshold } from '../../application/components/controls/select_severity/select_severity'; import { EXPLORER_ENTITY_FIELD_SELECTION_TRIGGER } from '../../ui_actions/triggers'; import type { MlLocatorParams } from '../../../common/types/locator'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '..'; +import { useAnomalyChartsData } from './use_anomaly_charts_data'; const RESIZE_THROTTLE_TIME_MS = 500; -export interface EmbeddableAnomalyChartsContainerProps { +export interface AnomalyChartsContainerProps + extends Partial { + lastReloadRequestTime?: number; + api: AnomalyChartsApi | AnomalyChartsAttachmentApi; id: string; - embeddableContext: InstanceType; - embeddableInput: Observable; services: AnomalyChartsEmbeddableServices; - refresh: Observable; - onInputChange: (input: Partial) => void; - onOutputChange: (output: Partial) => void; + timeRange$: Observable; onRenderComplete: () => void; - onLoading: () => void; + onLoading: (v: boolean) => void; onError: (error: Error) => void; } -export const EmbeddableAnomalyChartsContainer: FC = ({ +const AnomalyChartsContainer: FC = ({ id, - embeddableContext, - embeddableInput, + timeRange$, + severityThreshold, services, - refresh, - onInputChange, - onOutputChange, onRenderComplete, onError, onLoading, + api, }) => { - useEmbeddableExecutionContext( - services[0].executionContext, - embeddableInput, - ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, - id - ); - const [chartWidth, setChartWidth] = useState(0); const [severity, setSeverity] = useState( optionValueToThreshold( - embeddableContext.getInput().severityThreshold ?? ML_ANOMALY_THRESHOLD.WARNING + severityThreshold !== undefined ? severityThreshold : ML_ANOMALY_THRESHOLD.WARNING ) ); const [selectedEntities, setSelectedEntities] = useState(); const [{ uiSettings }, { data: dataServices, share, uiActions, charts: chartsService }] = services; const { timefilter } = dataServices.query.timefilter; + const timeRange = useObservable(timeRange$); const mlLocator = useMemo( () => share.url.locators.get(ML_APP_LOCATOR)!, @@ -95,32 +85,28 @@ export const EmbeddableAnomalyChartsContainer: FC { + if (api?.updateSeverityThreshold) { + api.updateSeverityThreshold(severity.val); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [severity.val, api?.updateSeverityThreshold]); useEffect(() => { - onInputChange({ - severityThreshold: severity.val, - }); - onOutputChange({ - severity: severity.val, - entityFields: selectedEntities, - }); + if (api?.updateSelectedEntities) { + api.updateSelectedEntities(selectedEntities); + } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [severity, selectedEntities]); + }, [selectedEntities, api?.updateSelectedEntities]); + const renderCallbacks = useMemo(() => { + return { onRenderComplete, onError, onLoading }; + }, [onRenderComplete, onError, onLoading]); const { chartsData, isLoading: isExplorerLoading, error, - } = useAnomalyChartsInputResolver( - embeddableInput, - onInputChange, - refresh, - services, - chartWidth, - severity.val, - { onRenderComplete, onError, onLoading } - ); + } = useAnomalyChartsData(api, services, chartWidth, severity.val, renderCallbacks); // Holds the container height for previously fetched data const containerHeightRef = useRef(); @@ -176,7 +162,7 @@ export const EmbeddableAnomalyChartsContainer: FC {isExplorerLoading && ( @@ -213,7 +199,7 @@ export const EmbeddableAnomalyChartsContainer: FC )} - {chartsData !== undefined && isExplorerLoading === false && ( + {chartsData !== undefined && isExplorerLoading === false ? ( - )} + ) : null} )} @@ -237,4 +223,4 @@ export const EmbeddableAnomalyChartsContainer: FC> { + pluginStart: MlStartDependencies, + parentApi: unknown, + focusedPanelId: string, + input?: Partial> +): Promise> { const { http, overlays, ...startServices } = coreStart; - - const { getJobs } = mlApiServicesProvider(new HttpService(http)); + const adJobsApiService = jobsApiProvider(new HttpService(http)); + const mlServices = getMlGlobalServices(http, pluginStart.data.dataViews); + const overlayTracker = tracksOverlays(parentApi) ? parentApi : undefined; return new Promise(async (resolve, reject) => { try { - const { jobIds } = await resolveJobSelection(coreStart, dataViews, input?.jobIds); - const title = input?.title ?? getDefaultExplorerChartsPanelTitle(jobIds); - const { jobs } = await getJobs({ jobId: jobIds.join(',') }); - const influencers = extractInfluencers(jobs); - influencers.push(VIEW_BY_JOB_LABEL); - const modalSession = overlays.openModal( + const flyoutSession = overlays.openFlyout( toMountPoint( - { - modalSession.close(); - resolve({ - jobIds, - title: panelTitle, - maxSeriesToPlot, - }); - }} - onCancel={() => { - modalSession.close(); - reject(); - }} - />, + + { + resolve({ + jobIds, + title, + maxSeriesToPlot, + } as Pick); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }} + onCancel={() => { + reject(); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }} + adJobsApiService={adJobsApiService} + /> + , startServices - ) + ), + { + type: 'push', + ownFocus: true, + size: 's', + onClose: () => { + reject(); + flyoutSession.close(); + overlayTracker?.clearOverlays(); + }, + 'data-test-subj': 'mlAnomalyChartsEmbeddableInitializer', + } ); + if (tracksOverlays(parentApi)) { + parentApi.openOverlay(flyoutSession, { + focusedPanelId, + }); + } } catch (error) { reject(error); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx deleted file mode 100644 index 0bca926ad6873..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container.test.tsx +++ /dev/null @@ -1,189 +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 from 'react'; -import { render } from '@testing-library/react'; -import type { EmbeddableAnomalyChartsContainerProps } from './embeddable_anomaly_charts_container'; -import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container'; -import type { Observable } from 'rxjs'; -import { BehaviorSubject, of } from 'rxjs'; -import { I18nProvider } from '@kbn/i18n-react'; -import type { AnomalyChartsEmbeddable } from './anomaly_charts_embeddable'; -import type { CoreStart } from '@kbn/core/public'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { MlDependencies } from '../../application/app'; -import type { TriggerContract } from '@kbn/ui-actions-plugin/public/triggers'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsServices } from '..'; -import { ExplorerAnomaliesContainer } from '../../application/explorer/explorer_charts/explorer_anomalies_container'; -import { createMlResultsServiceMock } from '../../application/services/ml_results_service'; -import { createCoreStartMock } from '../../__mocks__/core_start'; -import { createMlStartDepsMock } from '../../__mocks__/ml_start_deps'; -import { createAnomalyExplorerChartsServiceMock } from '../../application/services/__mocks__/anomaly_explorer_charts_service'; -import { createAnomalyDetectorServiceMock } from '../../application/services/__mocks__/anomaly_detector_service'; - -jest.mock('./use_anomaly_charts_input_resolver', () => ({ - useAnomalyChartsInputResolver: jest.fn(() => { - return []; - }), -})); - -jest.mock('../../application/explorer/explorer_charts/explorer_anomalies_container', () => ({ - ExplorerAnomaliesContainer: jest.fn(() => { - return null; - }), -})); - -const defaultOptions = { wrapper: I18nProvider }; - -describe('EmbeddableAnomalyChartsContainer', () => { - let embeddableInput: BehaviorSubject>; - let refresh: BehaviorSubject; - let services: jest.Mocked<[CoreStart, MlDependencies, AnomalyChartsServices]>; - let embeddableContext: jest.Mocked; - let trigger: jest.Mocked; - - const onInputChange = jest.fn(); - const onOutputChange = jest.fn(); - const onRenderComplete = jest.fn(); - const onLoading = jest.fn(); - const onError = jest.fn(); - - const mockedInput = { - viewMode: 'view', - filters: [], - hidePanelTitles: false, - query: { - language: 'lucene', - query: 'instance:i-d**', - }, - timeRange: { - from: 'now-3y', - to: 'now', - }, - refreshConfig: { - value: 0, - pause: true, - }, - id: 'b5b2f600-9c7e-4f7d-8b82-ee156fffad27', - searchSessionId: 'e8d052f8-0d9a-4d80-819d-fe18d9b314fa', - syncColors: true, - title: 'ML anomaly explorer charts for cw_multi_1', - jobIds: ['cw_multi_1'], - maxSeriesToPlot: 12, - enhancements: {}, - severity: 50, - severityThreshold: 75, - } as AnomalyChartsEmbeddableInput; - - beforeEach(() => { - // we only want to mock some of the functions needed - // @ts-ignore - embeddableContext = { - id: 'test-id', - getInput: jest.fn(), - }; - embeddableContext.getInput.mockReturnValue(mockedInput); - - embeddableInput = new BehaviorSubject({ - id: 'test-explorer-charts-embeddable', - } as Partial); - - trigger = { exec: jest.fn() } as unknown as jest.Mocked; - - const mlStartMock = createMlStartDepsMock(); - mlStartMock.uiActions.getTrigger.mockReturnValue(trigger); - - const coreStartMock = createCoreStartMock(); - const anomalyDetectorServiceMock = createAnomalyDetectorServiceMock(); - - anomalyDetectorServiceMock.getJobs$.mockImplementation((jobId: string[]) => { - if (jobId.includes('invalid-job-id')) { - throw new Error('Invalid job'); - } - return of([ - { - job_id: 'cw_multi_1', - analysis_config: { bucket_span: '15m' }, - }, - ]); - }); - - services = [ - coreStartMock, - mlStartMock, - { - anomalyDetectorService: anomalyDetectorServiceMock, - anomalyExplorerChartsService: createAnomalyExplorerChartsServiceMock(), - mlResultsService: createMlResultsServiceMock(), - }, - ] as unknown as EmbeddableAnomalyChartsContainerProps['services']; - }); - - test('should render explorer charts with a valid embeddable input', async () => { - const chartsData = { - chartsPerRow: 2, - seriesToPlot: [], - tooManyBuckets: false, - timeFieldName: '@timestamp', - errorMessages: undefined, - }; - - (useAnomalyChartsInputResolver as jest.Mock).mockReturnValueOnce({ - chartsData, - isLoading: false, - error: undefined, - }); - - render( - } - services={services} - refresh={refresh} - onInputChange={onInputChange} - onOutputChange={onOutputChange} - onLoading={onLoading} - onRenderComplete={onRenderComplete} - onError={onError} - />, - defaultOptions - ); - - const calledWith = ( - ExplorerAnomaliesContainer as unknown as jest.Mock - ).mock.calls[0][0]; - - expect(calledWith).toMatchSnapshot(); - }); - - test('should render an error in case it could not fetch the ML charts data', async () => { - (useAnomalyChartsInputResolver as jest.Mock).mockReturnValue({ - chartsData: undefined, - isLoading: false, - error: 'No anomalies', - }); - - const { findByText } = render( - } - services={services} - refresh={refresh} - onInputChange={onInputChange} - onOutputChange={onOutputChange} - onLoading={onLoading} - onRenderComplete={onRenderComplete} - onError={onError} - />, - defaultOptions - ); - const errorMessage = await findByText('Unable to load the data for the anomaly charts'); - expect(errorMessage).toBeDefined(); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts new file mode 100644 index 0000000000000..b0c365088beed --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts @@ -0,0 +1,64 @@ +/* + * 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 type { CoreStart } from '@kbn/core/public'; +import { HttpService } from '../../application/services/http_service'; +import type { MlStartDependencies } from '../../plugin'; +import type { MlDependencies } from '../../application/app'; +import type { AnomalyChartsEmbeddableServices } from '..'; +import { AnomalyExplorerChartsService } from '../../application/services/anomaly_explorer_charts_service'; + +export const getAnomalyChartsServiceDependencies = async ( + coreStartServices: CoreStart, + pluginsStartServices: MlStartDependencies +): Promise => { + const [ + { AnomalyDetectorService }, + { fieldFormatServiceFactory }, + { indexServiceFactory }, + { mlApiServicesProvider }, + { mlResultsServiceProvider }, + ] = await Promise.all([ + await import('../../application/services/anomaly_detector_service'), + await import('../../application/services/field_format_service_factory'), + await import('../../application/util/index_service'), + await import('../../application/services/ml_api_service'), + await import('../../application/services/results_service'), + ]); + const httpService = new HttpService(coreStartServices.http); + const anomalyDetectorService = new AnomalyDetectorService(httpService); + const mlApiServices = mlApiServicesProvider(httpService); + const mlResultsService = mlResultsServiceProvider(mlApiServices); + const anomalyExplorerService = new AnomalyExplorerChartsService( + pluginsStartServices.data.query.timefilter.timefilter, + mlApiServices, + mlResultsService + ); + + // Note on the following services: + // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`, + // but it's not being made available as part of global services. Since it's just + // some stateless utils `useMlIndexUtils()` should be used from within components. + // - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache", + // so because of its own state it needs to be made available as a global service. + // In the long run we should again try to get rid of it here and make it available via + // its own context or possibly without having a singleton like state at all, since the + // way this manages its own state right now doesn't consider React component lifecycles. + const mlIndexUtils = indexServiceFactory(pluginsStartServices.data.dataViews); + const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils); + + const anomalyChartsEmbeddableServices: AnomalyChartsEmbeddableServices = [ + coreStartServices, + pluginsStartServices as MlDependencies, + { + anomalyDetectorService, + anomalyExplorerService, + mlFieldFormatService, + mlResultsService, + }, + ]; + return anomalyChartsEmbeddableServices; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts index 7ee763b893367..71e5d9e71dd70 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { AnomalyChartsEmbeddableFactory } from './anomaly_charts_embeddable_factory'; +export { getAnomalyChartsReactEmbeddableFactory } from './anomaly_charts_embeddable_factory'; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts new file mode 100644 index 0000000000000..b5ed19391237b --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/initialize_anomaly_charts_controls.ts @@ -0,0 +1,100 @@ +/* + * 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 type { TitlesApi } from '@kbn/presentation-publishing/interfaces/titles/titles_api'; +import { BehaviorSubject } from 'rxjs'; +import fastIsEqual from 'fast-deep-equal'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; +import type { StateComparators } from '@kbn/presentation-publishing'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; +import { DEFAULT_MAX_SERIES_TO_PLOT } from '../../application/services/anomaly_explorer_charts_service'; +import type { + AnomalyChartsComponentApi, + AnomalyChartsDataLoadingApi, + AnomalyChartsEmbeddableRuntimeState, + AnomalyChartsEmbeddableState, +} from '../types'; + +export const initializeAnomalyChartsControls = ( + rawState: AnomalyChartsEmbeddableState, + titlesApi?: TitlesApi, + parentApi?: unknown +) => { + const jobIds$ = new BehaviorSubject(rawState.jobIds); + const maxSeriesToPlot$ = new BehaviorSubject( + rawState.maxSeriesToPlot ?? DEFAULT_MAX_SERIES_TO_PLOT + ); + const severityThreshold$ = new BehaviorSubject(rawState.severityThreshold); + const selectedEntities$ = new BehaviorSubject( + rawState.selectedEntities + ); + const interval$ = new BehaviorSubject(undefined); + const dataLoading$ = new BehaviorSubject(true); + const blockingError$ = new BehaviorSubject(undefined); + + const updateUserInput = (update: AnomalyChartsEmbeddableState) => { + jobIds$.next(update.jobIds); + maxSeriesToPlot$.next(update.maxSeriesToPlot); + if (titlesApi) { + titlesApi.setPanelTitle(update.title); + } + }; + + const updateSeverityThreshold = (v: number) => severityThreshold$.next(v); + const updateSelectedEntities = (v: MlEntityField[]) => selectedEntities$.next(v); + const setInterval = (v: number) => interval$.next(v); + + const serializeAnomalyChartsState = (): AnomalyChartsEmbeddableState => { + return { + jobIds: jobIds$.value, + maxSeriesToPlot: maxSeriesToPlot$.value, + severityThreshold: severityThreshold$.value, + selectedEntities: selectedEntities$.value, + }; + }; + + const anomalyChartsComparators: StateComparators = { + jobIds: [jobIds$, (arg: JobId[]) => jobIds$.next(arg), fastIsEqual], + maxSeriesToPlot: [maxSeriesToPlot$, (arg: number) => maxSeriesToPlot$.next(arg)], + severityThreshold: [severityThreshold$, (arg?: number) => severityThreshold$.next(arg)], + selectedEntities: [ + selectedEntities$, + (arg?: MlEntityField[]) => selectedEntities$.next(arg), + fastIsEqual, + ], + }; + const onRenderComplete = () => dataLoading$.next(false); + const onLoading = (v: boolean) => dataLoading$.next(v); + const onError = (error?: Error) => blockingError$.next(error); + + return { + anomalyChartsControlsApi: { + jobIds$, + maxSeriesToPlot$, + severityThreshold$, + selectedEntities$, + updateUserInput, + updateSeverityThreshold, + updateSelectedEntities, + } as AnomalyChartsComponentApi, + dataLoadingApi: { + dataLoading: dataLoading$, + setInterval, + onRenderComplete, + onLoading, + onError, + } as AnomalyChartsDataLoadingApi, + serializeAnomalyChartsState, + anomalyChartsComparators, + onAnomalyChartsDestroy: () => { + jobIds$.complete(); + maxSeriesToPlot$.complete(); + severityThreshold$.complete(); + selectedEntities$.complete(); + }, + }; +}; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts similarity index 62% rename from x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx rename to x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts index 38f48ea4a018b..76abe473f3c9e 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/embeddable_anomaly_charts_container_lazy.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/lazy_anomaly_charts_container.ts @@ -4,9 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { dynamic } from '@kbn/shared-ux-utility'; -import React from 'react'; - -export const EmbeddableAnomalyChartsContainer = React.lazy( - () => import('./embeddable_anomaly_charts_container') -); +export const LazyAnomalyChartsContainer = dynamic(() => import('./anomaly_charts_react_container')); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts deleted file mode 100644 index 0c6ddf3a9e35c..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { MlEntityField } from '@kbn/ml-anomaly-utils'; -import type { PublishingSubject } from '@kbn/presentation-publishing'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import type { MlEmbeddableBaseApi } from '../types'; - -export interface AnomalyChartsFieldSelectionApi { - jobIds: PublishingSubject; - entityFields: PublishingSubject; -} - -export interface AnomalyChartsEmbeddableApi - extends MlEmbeddableBaseApi, - AnomalyChartsFieldSelectionApi {} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts new file mode 100644 index 0000000000000..ee7acdba2ab55 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_data.ts @@ -0,0 +1,170 @@ +/* + * 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 { useState, useMemo, useEffect } from 'react'; +import { combineLatest, tap, debounceTime, switchMap, skipWhile, of } from 'rxjs'; +import { Subject, catchError } from 'rxjs'; +import type { InfluencersFilterQuery } from '@kbn/ml-anomaly-utils'; +import type { CoreStart } from '@kbn/core/public'; +import { fetch$ } from '@kbn/presentation-publishing'; +import type { AnomalyChartsServices, AnomalyChartsApi } from '..'; +import { getJobsObservable } from '../common/get_jobs_observable'; +import { OVERALL_LABEL, SWIMLANE_TYPE } from '../../application/explorer/explorer_constants'; +import { processFilters } from '../common/process_filters'; +import type { AppStateSelectedCells } from '../../application/explorer/explorer_utils'; +import { + getSelectionInfluencers, + getSelectionJobIds, + getSelectionTimeRange, +} from '../../application/explorer/explorer_utils'; +import type { ExplorerChartsData } from '../../application/explorer/explorer_charts/explorer_charts_container_service'; +import type { MlStartDependencies } from '../../plugin'; + +const FETCH_RESULTS_DEBOUNCE_MS = 500; + +export function useAnomalyChartsData( + api: AnomalyChartsApi, + services: [CoreStart, MlStartDependencies, AnomalyChartsServices], + chartWidth: number, + severity: number, + renderCallbacks: { + onRenderComplete: () => void; + onLoading: (v: boolean) => void; + onError: (error: Error) => void; + } +): { + chartsData: ExplorerChartsData | undefined; + isLoading: boolean; + error: Error | null | undefined; +} { + const [, , { anomalyDetectorService, anomalyExplorerService }] = services; + + const [chartsData, setChartsData] = useState(); + const [error, setError] = useState(); + const [isLoading, setIsLoading] = useState(false); + + const chartWidth$ = useMemo(() => new Subject(), []); + const severity$ = useMemo(() => new Subject(), []); + + useEffect(() => { + const subscription = combineLatest({ + explorerJobs: getJobsObservable(api.jobIds$, anomalyDetectorService, setError), + maxSeriesToPlot: api.maxSeriesToPlot$, + chartWidth: chartWidth$.pipe(skipWhile((v) => !v)), + severityValue: severity$, + dataPublishingServices: fetch$(api), + }) + .pipe( + tap(setIsLoading.bind(null, true)), + debounceTime(FETCH_RESULTS_DEBOUNCE_MS), + tap(() => { + renderCallbacks.onLoading(true); + }), + switchMap( + ({ + explorerJobs, + maxSeriesToPlot, + chartWidth: embeddableContainerWidth, + severityValue, + dataPublishingServices, + }) => { + const { timeRange: timeRangeInput, filters, query } = dataPublishingServices; + if (!explorerJobs) { + // couldn't load the list of jobs + return of(undefined); + } + + const viewBySwimlaneFieldName = OVERALL_LABEL; + + if (timeRangeInput) { + anomalyExplorerService.setTimeRange(timeRangeInput); + } + + let influencersFilterQuery: InfluencersFilterQuery | undefined; + try { + if (filters || query) { + influencersFilterQuery = processFilters(filters, query); + } + } catch (e) { + // handle query syntax errors + setError(e); + return of(undefined); + } + + const bounds = anomalyExplorerService.getTimeBounds(); + + // Can be from input time range or from the timefilter bar + const selections: AppStateSelectedCells = { + lanes: [OVERALL_LABEL], + times: [bounds.min?.unix()!, bounds.max?.unix()!], + type: SWIMLANE_TYPE.OVERALL, + }; + + const selectionInfluencers = getSelectionInfluencers( + selections, + viewBySwimlaneFieldName + ); + + const jobIds = getSelectionJobIds(selections, explorerJobs); + + const timeRange = getSelectionTimeRange(selections, bounds); + + return anomalyExplorerService.getAnomalyData$( + jobIds, + embeddableContainerWidth, + timeRange.earliestMs, + timeRange.latestMs, + influencersFilterQuery, + selectionInfluencers, + severityValue ?? 0, + maxSeriesToPlot + ); + } + ), + catchError((e) => { + // eslint-disable-next-line no-console + console.error(`Error occured fetching anomaly charts data for embeddable\n`, e); + setError(e.body); + return of(undefined); + }) + ) + .subscribe((results) => { + if (results !== undefined) { + setError(null); + setChartsData(results); + setIsLoading(false); + renderCallbacks.onRenderComplete(); + } + }); + + return () => { + subscription?.unsubscribe(); + chartWidth$.complete(); + severity$.complete(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + chartWidth$.next(chartWidth); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [chartWidth]); + + useEffect(() => { + severity$.next(severity); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [severity]); + + useEffect(() => { + if (error) { + renderCallbacks.onError(error); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [error]); + + return { chartsData, isLoading, error }; +} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts deleted file mode 100644 index 2ddb7b75f3d6d..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.test.ts +++ /dev/null @@ -1,197 +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 { renderHook } from '@testing-library/react-hooks'; -import type { Observable } from 'rxjs'; -import { BehaviorSubject, of, Subject } from 'rxjs'; -import { fakeSchedulers } from 'rxjs-marbles/jest'; -import type { AnomalyChartsEmbeddableInput, AnomalyChartsServices } from '../types'; -import type { CoreStart } from '@kbn/core/public'; -import type { MlStartDependencies } from '../../plugin'; -import { useAnomalyChartsInputResolver } from './use_anomaly_charts_input_resolver'; -import type { EmbeddableAnomalyChartsContainerProps } from './embeddable_anomaly_charts_container'; -import moment from 'moment'; -import { createMlResultsServiceMock } from '../../application/services/ml_results_service'; -import { createCoreStartMock } from '../../__mocks__/core_start'; -import { createMlStartDepsMock } from '../../__mocks__/ml_start_deps'; -import { createAnomalyExplorerChartsServiceMock } from '../../application/services/__mocks__/anomaly_explorer_charts_service'; -import { createAnomalyDetectorServiceMock } from '../../application/services/__mocks__/anomaly_detector_service'; - -jest.mock('../common/process_filters', () => ({ - processFilters: jest.fn(), -})); - -jest.mock('../../application/explorer/explorer_utils', () => ({ - getSelectionInfluencers: jest.fn(() => { - return []; - }), - getSelectionJobIds: jest.fn(() => ['test-job']), - getSelectionTimeRange: jest.fn(() => ({ earliestMs: 1521309543000, latestMs: 1616003942999 })), -})); - -describe('useAnomalyChartsInputResolver', () => { - let embeddableInput: BehaviorSubject>; - let refresh: Subject; - let services: [CoreStart, MlStartDependencies, AnomalyChartsServices]; - let onInputChange: jest.Mock; - - const start = moment().subtract(1, 'years'); - const end = moment(); - - const renderCallbacks = { - onRenderComplete: jest.fn(), - onLoading: jest.fn(), - onError: jest.fn(), - }; - - beforeEach(() => { - jest.useFakeTimers({ legacyFakeTimers: true }); - - const jobIds = ['test-job']; - embeddableInput = new BehaviorSubject({ - id: 'test-explorer-charts-embeddable', - jobIds, - filters: [], - query: { language: 'kuery', query: '' }, - maxSeriesToPlot: 12, - timeRange: { - from: 'now-3y', - to: 'now', - }, - } as Partial); - - refresh = new Subject(); - const anomalyExplorerChartsServiceMock = createAnomalyExplorerChartsServiceMock(); - - anomalyExplorerChartsServiceMock.getTimeBounds.mockReturnValue({ - min: start, - max: end, - }); - - anomalyExplorerChartsServiceMock.getAnomalyData$.mockImplementation(() => - of({ - chartsPerRow: 2, - seriesToPlot: [], - tooManyBuckets: false, - timeFieldName: '@timestamp', - errorMessages: undefined, - }) - ); - - const coreStartMock = createCoreStartMock(); - const mlStartMock = createMlStartDepsMock(); - - const anomalyDetectorServiceMock = createAnomalyDetectorServiceMock(); - anomalyDetectorServiceMock.getJobs$.mockImplementation((jobId: string[]) => { - if (jobId.includes('invalid-job-id')) { - throw new Error('Invalid job'); - } - return of([ - { - job_id: 'cw_multi_1', - analysis_config: { bucket_span: '15m' }, - }, - ]); - }); - - services = [ - coreStartMock, - mlStartMock, - { - anomalyDetectorService: anomalyDetectorServiceMock, - anomalyExplorerService: anomalyExplorerChartsServiceMock, - mlResultsService: createMlResultsServiceMock(), - }, - ] as unknown as EmbeddableAnomalyChartsContainerProps['services']; - - onInputChange = jest.fn(); - }); - - afterEach(() => { - jest.useRealTimers(); - jest.clearAllMocks(); - }); - - test( - 'should fetch jobs only when input job ids have been changed', - fakeSchedulers(async (advance) => { - const { result } = renderHook(() => - useAnomalyChartsInputResolver( - embeddableInput as Observable, - onInputChange, - refresh, - services, - 1000, - 0, - renderCallbacks - ) - ); - - expect(result.current.chartsData).toBe(undefined); - expect(result.current.error).toBe(undefined); - expect(result.current.isLoading).toBe(true); - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(0); - - advance(501); - - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(1); - - const explorerServices = services[2]; - - expect(explorerServices.anomalyDetectorService.getJobs$).toHaveBeenCalledTimes(1); - expect(explorerServices.anomalyExplorerService.getAnomalyData$).toHaveBeenCalledTimes(1); - - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(1); - - embeddableInput.next({ - id: 'test-explorer-charts-embeddable', - jobIds: ['anotherJobId'], - filters: [], - query: { language: 'kuery', query: '' }, - maxSeriesToPlot: 6, - timeRange: { - from: 'now-3y', - to: 'now', - }, - }); - advance(501); - - expect(renderCallbacks.onLoading).toHaveBeenCalledTimes(2); - - expect(explorerServices.anomalyDetectorService.getJobs$).toHaveBeenCalledTimes(2); - expect(explorerServices.anomalyExplorerService.getAnomalyData$).toHaveBeenCalledTimes(2); - - expect(renderCallbacks.onRenderComplete).toHaveBeenCalledTimes(2); - - expect(renderCallbacks.onError).toHaveBeenCalledTimes(0); - }) - ); - - test.skip('should not complete the observable on error', async () => { - const { result } = renderHook(() => - useAnomalyChartsInputResolver( - embeddableInput as Observable, - onInputChange, - refresh, - services, - 1000, - 1, - renderCallbacks - ) - ); - - embeddableInput.next({ - id: 'test-explorer-charts-embeddable', - jobIds: ['invalid-job-id'], - filters: [], - query: { language: 'kuery', query: '' }, - } as Partial); - - expect(result.current.error).toBeDefined(); - expect(renderCallbacks.onError).toHaveBeenCalledTimes(1); - }); -}); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts deleted file mode 100644 index 7af34daec557d..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/use_anomaly_charts_input_resolver.ts +++ /dev/null @@ -1,161 +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, useMemo, useState } from 'react'; -import type { Observable } from 'rxjs'; -import { combineLatest, of, Subject } from 'rxjs'; -import { catchError, debounceTime, skipWhile, startWith, switchMap, tap } from 'rxjs'; -import type { CoreStart } from '@kbn/core/public'; -import type { InfluencersFilterQuery } from '@kbn/ml-anomaly-utils'; -import type { MlStartDependencies } from '../../plugin'; -import type { AppStateSelectedCells } from '../../application/explorer/explorer_utils'; -import { - getSelectionInfluencers, - getSelectionJobIds, - getSelectionTimeRange, -} from '../../application/explorer/explorer_utils'; -import { OVERALL_LABEL, SWIMLANE_TYPE } from '../../application/explorer/explorer_constants'; -import type { - AnomalyChartsEmbeddableInput, - AnomalyChartsEmbeddableOutput, - AnomalyChartsServices, -} from '..'; -import type { ExplorerChartsData } from '../../application/explorer/explorer_charts/explorer_charts_container_service'; -import { processFilters } from '../common/process_filters'; -import { getJobsObservable } from '../common/get_jobs_observable'; - -const FETCH_RESULTS_DEBOUNCE_MS = 500; - -export function useAnomalyChartsInputResolver( - embeddableInput: Observable, - onInputChange: (output: Partial) => void, - refresh: Observable, - services: [CoreStart, MlStartDependencies, AnomalyChartsServices], - chartWidth: number, - severity: number, - renderCallbacks: { - onRenderComplete: () => void; - onLoading: () => void; - onError: (error: Error) => void; - } -): { - chartsData: ExplorerChartsData | undefined; - isLoading: boolean; - error: Error | null | undefined; -} { - const [, , { anomalyDetectorService, anomalyExplorerService }] = services; - - const [chartsData, setChartsData] = useState(); - const [error, setError] = useState(); - const [isLoading, setIsLoading] = useState(false); - - const chartWidth$ = useMemo(() => new Subject(), []); - const severity$ = useMemo(() => new Subject(), []); - - useEffect(() => { - const subscription = combineLatest([ - getJobsObservable(embeddableInput, anomalyDetectorService, setError), - embeddableInput, - chartWidth$.pipe(skipWhile((v) => !v)), - severity$, - refresh.pipe(startWith(null)), - ]) - .pipe( - tap(setIsLoading.bind(null, true)), - debounceTime(FETCH_RESULTS_DEBOUNCE_MS), - tap(() => { - renderCallbacks.onLoading(); - }), - switchMap(([explorerJobs, input, embeddableContainerWidth, severityValue]) => { - if (!explorerJobs) { - // couldn't load the list of jobs - return of(undefined); - } - - const { maxSeriesToPlot, timeRange: timeRangeInput, filters, query } = input; - - const viewBySwimlaneFieldName = OVERALL_LABEL; - - anomalyExplorerService.setTimeRange(timeRangeInput); - - let influencersFilterQuery: InfluencersFilterQuery | undefined; - try { - if (filters || query) { - influencersFilterQuery = processFilters(filters, query); - } - } catch (e) { - // handle query syntax errors - setError(e); - return of(undefined); - } - - const bounds = anomalyExplorerService.getTimeBounds(); - - // Can be from input time range or from the timefilter bar - const selections: AppStateSelectedCells = { - lanes: [OVERALL_LABEL], - times: [bounds.min?.unix()!, bounds.max?.unix()!], - type: SWIMLANE_TYPE.OVERALL, - }; - - const selectionInfluencers = getSelectionInfluencers(selections, viewBySwimlaneFieldName); - - const jobIds = getSelectionJobIds(selections, explorerJobs); - - const timeRange = getSelectionTimeRange(selections, bounds); - - return anomalyExplorerService.getAnomalyData$( - jobIds, - embeddableContainerWidth, - timeRange.earliestMs, - timeRange.latestMs, - influencersFilterQuery, - selectionInfluencers, - severityValue ?? 0, - maxSeriesToPlot - ); - }), - catchError((e) => { - setError(e.body); - return of(undefined); - }) - ) - .subscribe((results) => { - if (results !== undefined) { - setError(null); - setChartsData(results); - setIsLoading(false); - - renderCallbacks.onRenderComplete(); - } - }); - - return () => { - subscription.unsubscribe(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - chartWidth$.next(chartWidth); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [chartWidth]); - - useEffect(() => { - severity$.next(severity); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [severity]); - - useEffect(() => { - if (error) { - renderCallbacks.onError(error); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [error]); - - return { chartsData, isLoading, error }; -} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts new file mode 100644 index 0000000000000..82355a2ef3d71 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/utils.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; + +export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) => + i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', { + defaultMessage: 'ML anomaly charts for {jobIds}', + values: { jobIds: jobIds.join(', ') }, + }); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx index 3c13a85cd6286..c67c2ed157cda 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable_factory.tsx @@ -199,7 +199,6 @@ export const getAnomalySwimLaneEmbeddableFactory = ( ...swimLaneComparators, } ); - const appliedTimeRange$: Observable = combineLatest([ api.timeRange$, apiHasParentApi(api) && apiPublishesTimeRange(api.parentApi) diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx index 64c0d9db7b7b7..52c84770da12c 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_initializer.tsx @@ -33,6 +33,7 @@ import type { SwimlaneType } from '../../application/explorer/explorer_constants import { SWIMLANE_TYPE, VIEW_BY_JOB_LABEL } from '../../application/explorer/explorer_constants'; import type { AnomalySwimLaneEmbeddableState, AnomalySwimlaneEmbeddableUserInput } from '..'; import { getDefaultSwimlanePanelTitle } from './anomaly_swimlane_embeddable'; +import { getJobSelectionErrors } from '../utils'; export type ExplicitInput = AnomalySwimlaneEmbeddableUserInput; @@ -45,16 +46,6 @@ export interface AnomalySwimlaneInitializerProps { adJobsApiService: MlApiServices['jobs']; } -const getJobSelectionErrors = (jobIds: string[]) => { - if (jobIds.length === 0) { - return [ - i18n.translate('xpack.ml.swimlaneEmbeddable.setupModal.jobSelectionRequiredError', { - defaultMessage: 'Job selection is required', - }), - ]; - } -}; - export const AnomalySwimlaneInitializer: FC = ({ onCreate, onCancel, diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts index 6946215b0c452..268a17fca4a81 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/initialize_swim_lane_data_fetcher.ts @@ -53,13 +53,9 @@ export const initializeSwimLaneDataFetcher = ( const swimLaneData$ = new BehaviorSubject(undefined); - const selectedJobs$ = getJobsObservable( - swimLaneApi.jobIds.pipe(map((jobIds) => ({ jobIds }))), - anomalyDetectorService, - (error) => { - blockingError.next(error); - } - ).pipe(shareReplay(1)); + const selectedJobs$ = getJobsObservable(swimLaneApi.jobIds, anomalyDetectorService, (error) => { + blockingError.next(error); + }).pipe(shareReplay(1)); const swimLaneInput$ = combineLatest({ jobIds: swimLaneApi.jobIds, diff --git a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts index e770106200f7b..e1fc6a6d178c1 100644 --- a/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts +++ b/x-pack/plugins/ml/public/embeddables/common/get_jobs_observable.ts @@ -14,22 +14,21 @@ import type { ExplorerJob } from '../../application/explorer/explorer_utils'; import type { AnomalyDetectorService } from '../../application/services/anomaly_detector_service'; export function getJobsObservable( - embeddableInput: Observable<{ jobIds: JobId[] }>, + jobIds$: Observable, anomalyDetectorService: AnomalyDetectorService, setErrorHandler: (e: Error) => void -) { - return embeddableInput.pipe( - map((v) => v.jobIds), +): Observable { + return jobIds$.pipe( distinctUntilChanged(isEqual), - switchMap((jobsIds) => - anomalyDetectorService.getJobs$(jobsIds).pipe( + switchMap((jobsIds) => { + return anomalyDetectorService.getJobs$(jobsIds).pipe( catchError((e) => { // Catch error to prevent the observable from completing setErrorHandler(e.body ?? e); return EMPTY; }) - ) - ), + ); + }), map((jobs) => { const explorerJobs: ExplorerJob[] = jobs.map((job) => { const bucketSpan = parseInterval(job.analysis_config.bucket_span!); diff --git a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts index d3725d59b7c66..e4b6079bfe0ea 100644 --- a/x-pack/plugins/ml/public/embeddables/common/process_filters.ts +++ b/x-pack/plugins/ml/public/embeddables/common/process_filters.ts @@ -10,6 +10,7 @@ import type { AggregateQuery, Filter, Query } from '@kbn/es-query'; import { isOfAggregateQueryType } from '@kbn/es-query'; import { fromKueryExpression, luceneStringToDsl, toElasticsearchQuery } from '@kbn/es-query'; import { getDefaultQuery } from '@kbn/data-plugin/public'; +import { isDefined } from '@kbn/ml-is-defined'; export function processFilters( optionalFilters?: Filter[], @@ -28,7 +29,7 @@ export function processFilters( : luceneStringToDsl(query.query); } - const must = [inputQuery]; + const must = isDefined(inputQuery) ? [inputQuery] : []; const mustNot = []; for (const filter of filters) { diff --git a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx b/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.tsx deleted file mode 100644 index 99b301ee19e9f..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/common/resolve_job_selection.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 type { CoreStart } from '@kbn/core/public'; -import moment from 'moment'; -import { takeUntil, distinctUntilChanged, skip } from 'rxjs'; -import { from } from 'rxjs'; -import React from 'react'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import { toMountPoint } from '@kbn/react-kibana-mount'; -import type { DataViewsContract } from '@kbn/data-views-plugin/public'; -import { getInitialGroupsMap } from '../../application/components/job_selector/job_selector'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import { JobSelectorFlyout } from './components/job_selector_flyout'; -import { getMlGlobalServices } from '../../application/util/get_services'; - -/** - * Handles Anomaly detection jobs selection by a user. - * Intended to use independently of the ML app context, - * for instance on the dashboard for embeddables initialization. - * - * @param coreStart - * @param selectedJobIds - */ -export async function resolveJobSelection( - coreStart: CoreStart, - dataViews: DataViewsContract, - selectedJobIds?: JobId[], - singleSelection: boolean = false -): Promise<{ jobIds: string[]; groups: Array<{ groupId: string; jobIds: string[] }> }> { - const { - http, - uiSettings, - application: { currentAppId$ }, - ...startServices - } = coreStart; - - return new Promise(async (resolve, reject) => { - try { - const maps = { - groupsMap: getInitialGroupsMap([]), - jobsMap: {}, - }; - const tzConfig = uiSettings.get('dateFormat:tz'); - const dateFormatTz = tzConfig !== 'Browser' ? tzConfig : moment.tz.guess(); - - const onFlyoutClose = () => { - flyoutSession.close(); - reject(); - }; - - const onSelectionConfirmed = async ({ - jobIds, - groups, - }: { - jobIds: string[]; - groups: Array<{ - groupId: string; - jobIds: string[]; - }>; - }) => { - await flyoutSession.close(); - resolve({ - jobIds, - groups, - }); - }; - - const flyoutSession = coreStart.overlays.openFlyout( - toMountPoint( - - - , - startServices - ), - { - 'data-test-subj': 'mlFlyoutJobSelector', - ownFocus: true, - closeButtonProps: { 'aria-label': 'jobSelectorFlyout' }, - } - ); - - // Close the flyout when user navigates out of the current plugin - currentAppId$ - .pipe(skip(1), takeUntil(from(flyoutSession.onClose)), distinctUntilChanged()) - .subscribe(() => { - flyoutSession.close(); - }); - } catch (error) { - reject(error); - } - }); -} diff --git a/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx b/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx deleted file mode 100644 index 4fc41a65efd1c..0000000000000 --- a/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiLoadingChart } from '@elastic/eui'; -import type { CoreStart } from '@kbn/core/public'; -import type { EmbeddableFactory, EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import { EmbeddableRoot, useEmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import React from 'react'; -import type { MlStartDependencies } from '../plugin'; -import type { AnomalyExplorerChartsEmbeddableType } from './constants'; -import type { MappedEmbeddableTypeOf } from './types'; - -/** - * Gets an instance of an embeddable component of requested type. - * @param embeddableType - * @param core - * @param plugins - */ -export function getEmbeddableComponent( - embeddableType: EmbeddableType, - core: CoreStart, - plugins: MlStartDependencies -) { - const { embeddable: embeddableStart } = plugins; - - const factory = - embeddableStart.getEmbeddableFactory>(embeddableType); - - if (!factory) { - throw new Error(`Embeddable type "${embeddableType}" has not been registered.`); - } - - return React.memo((props: MappedEmbeddableTypeOf) => { - return ; - }); -} - -interface EmbeddableRootWrapperProps { - factory: EmbeddableFactory; - input: TMlEmbeddableInput; -} - -const EmbeddableRootWrapper = ({ - factory, - input, -}: EmbeddableRootWrapperProps) => { - const [embeddable, loading, error] = useEmbeddableFactory({ factory, input }); - if (loading) { - return ; - } - return ; -}; diff --git a/x-pack/plugins/ml/public/embeddables/index.ts b/x-pack/plugins/ml/public/embeddables/index.ts index 936c76bc02703..9145bbddf5818 100644 --- a/x-pack/plugins/ml/public/embeddables/index.ts +++ b/x-pack/plugins/ml/public/embeddables/index.ts @@ -7,12 +7,13 @@ import type { EmbeddableSetup } from '@kbn/embeddable-plugin/public'; import type { MlCoreSetup } from '../plugin'; -import { AnomalyChartsEmbeddableFactory } from './anomaly_charts'; -import { ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE } from './constants'; -import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from './constants'; +import { + ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE, + ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, + ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, +} from './constants'; export * from './constants'; -export { getEmbeddableComponent } from './get_embeddable_component'; export * from './types'; export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSetup) { @@ -21,8 +22,12 @@ export function registerEmbeddables(embeddable: EmbeddableSetup, core: MlCoreSet return getAnomalySwimLaneEmbeddableFactory(core.getStartServices); }); - const anomalyChartsFactory = new AnomalyChartsEmbeddableFactory(core.getStartServices); - embeddable.registerEmbeddableFactory(anomalyChartsFactory.type, anomalyChartsFactory); + embeddable.registerReactEmbeddableFactory(ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, async () => { + const { getAnomalyChartsReactEmbeddableFactory } = await import( + './anomaly_charts/anomaly_charts_embeddable_factory' + ); + return getAnomalyChartsReactEmbeddableFactory(core.getStartServices); + }); embeddable.registerReactEmbeddableFactory( ANOMALY_SINGLE_METRIC_VIEWER_EMBEDDABLE_TYPE, diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 34e0b1afb3265..94389c69c20fc 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -7,13 +7,7 @@ import type { CoreStart } from '@kbn/core/public'; import type { RefreshInterval } from '@kbn/data-plugin/common'; -import type { DataView } from '@kbn/data-views-plugin/common'; -import type { - DefaultEmbeddableApi, - EmbeddableInput, - EmbeddableOutput, - IEmbeddable, -} from '@kbn/embeddable-plugin/public'; +import type { DefaultEmbeddableApi, EmbeddableInput } from '@kbn/embeddable-plugin/public'; import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import type { @@ -25,8 +19,10 @@ import type { PublishingSubject, PublishesTimeRange, PublishesWritablePanelTitle, + PublishesDataViews, SerializedTitles, } from '@kbn/presentation-publishing'; +import { type BehaviorSubject } from 'rxjs'; import type { JobId } from '../../common/types/anomaly_detection_jobs'; import type { MlDependencies } from '../application/app'; import type { MlCapabilitiesService } from '../application/capabilities/check_capabilities'; @@ -108,21 +104,74 @@ export interface SwimLaneDrilldownContext extends EditSwimlanePanelContext { } /** - * Anomaly Explorer + * Anomaly Explorer Charts */ -export interface AnomalyChartsEmbeddableCustomInput { + +export interface AnomalyChartsEmbeddableRuntimeState { jobIds: JobId[]; maxSeriesToPlot: number; - // Embeddable inputs which are not included in the default interface - filters: Filter[]; - query: Query; - refreshConfig: RefreshInterval; - timeRange: TimeRange; severityThreshold?: number; + selectedEntities?: MlEntityField[]; +} +export interface AnomalyChartsEmbeddableOverridableState + extends AnomalyChartsEmbeddableRuntimeState { + timeRange?: TimeRange; +} +export interface AnomalyChartsComponentApi { + jobIds$: PublishingSubject; + maxSeriesToPlot$: PublishingSubject; + severityThreshold$: PublishingSubject; + selectedEntities$: PublishingSubject; + updateUserInput: (input: AnomalyChartsEmbeddableOverridableState) => void; + updateSeverityThreshold: (v?: number) => void; + updateSelectedEntities: (entities?: MlEntityField[] | undefined) => void; +} +export interface AnomalyChartsDataLoadingApi { + onRenderComplete: () => void; + onLoading: (v: boolean) => void; + onError: (error?: Error) => void; } -export type AnomalyChartsEmbeddableInput = EmbeddableInput & AnomalyChartsEmbeddableCustomInput; +/** + * Persisted state for the Anomaly Charts Embeddable. + */ +export interface AnomalyChartsEmbeddableState + extends SerializedTitles, + AnomalyChartsEmbeddableOverridableState {} + +export type AnomalyChartsApi = AnomalyChartsComponentApi & AnomalyChartsDataLoadingApi; + +export type AnomalyChartsEmbeddableApi = MlEmbeddableBaseApi & + PublishesDataViews & + PublishesWritablePanelTitle & + HasEditCapabilities & + AnomalyChartsApi; + +export interface AnomalyChartsFieldSelectionApi { + jobIds: PublishingSubject; + entityFields: PublishingSubject; +} + +export interface AnomalyChartsAttachmentState extends AnomalyChartsEmbeddableState { + query?: Query; + filters?: Filter[]; +} + +export interface AnomalyChartsAttachmentApi extends AnomalyChartsApi { + parentApi: { + query$: BehaviorSubject; + filters$: BehaviorSubject; + timeRange$: BehaviorSubject; + }; +} + +/** + * Persisted state for the Anomaly Charts Embeddable. + */ +export interface AnomalyChartsEmbeddableState + extends SerializedTitles, + AnomalyChartsEmbeddableOverridableState {} /** Manual input by the user */ export interface SingleMetricViewerEmbeddableUserInput { @@ -205,14 +254,8 @@ export type SingleMetricViewerEmbeddableServices = [ MlDependencies, SingleMetricViewerServices ]; -export interface AnomalyChartsCustomOutput { - entityFields?: MlEntityField[]; - severity?: number; - indexPatterns: DataView[]; -} -export type AnomalyChartsEmbeddableOutput = EmbeddableOutput & AnomalyChartsCustomOutput; export interface EditAnomalyChartsPanelContext { - embeddable: IEmbeddable; + embeddable: AnomalyChartsEmbeddableApi; } export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPanelContext { @@ -224,5 +267,5 @@ export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPan export type MappedEmbeddableTypeOf = TEmbeddableType extends AnomalyExplorerChartsEmbeddableType - ? AnomalyChartsEmbeddableInput + ? AnomalyChartsEmbeddableState : unknown; diff --git a/x-pack/plugins/ml/public/embeddables/utils.ts b/x-pack/plugins/ml/public/embeddables/utils.ts new file mode 100644 index 0000000000000..807a0d8a7b8d2 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/utils.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; + +export const getJobSelectionErrors = (jobIds: string[]) => { + if (jobIds.length === 0) { + return [ + i18n.translate('xpack.ml.swimlaneEmbeddable.setupModal.jobSelectionRequiredError', { + defaultMessage: 'Job selection is required', + }), + ]; + } +}; diff --git a/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx new file mode 100644 index 0000000000000..c4b88bdd43306 --- /dev/null +++ b/x-pack/plugins/ml/public/ui_actions/create_anomaly_chart.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import type { PresentationContainer } from '@kbn/presentation-containers'; +import type { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; +import { ML_APP_NAME, PLUGIN_ICON, PLUGIN_ID } from '../../common/constants/app'; +import type { AnomalyChartsEmbeddableApi } from '../embeddables'; +import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; +import type { MlCoreSetup } from '../plugin'; + +export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction'; + +export type CreateAnomalyChartsPanelActionContext = EmbeddableApiContext & { + embeddable: AnomalyChartsEmbeddableApi; +}; + +const parentApiIsCompatible = async ( + parentApi: unknown +): Promise => { + const { apiIsPresentationContainer } = await import('@kbn/presentation-containers'); + // we cannot have an async type check, so return the casted parentApi rather than a boolean + return apiIsPresentationContainer(parentApi) ? (parentApi as PresentationContainer) : undefined; +}; + +export function createAddAnomalyChartsPanelAction( + getStartServices: MlCoreSetup['getStartServices'] +): UiActionsActionDefinition { + return { + id: 'create-anomaly-charts', + grouping: [ + { + id: PLUGIN_ID, + getDisplayName: () => ML_APP_NAME, + getIconType: () => PLUGIN_ICON, + }, + ], + getDisplayName: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.displayName', { + defaultMessage: 'Anomaly chart', + }), + getDisplayNameTooltip: () => + i18n.translate('xpack.ml.components.mlAnomalyExplorerEmbeddable.description', { + defaultMessage: 'View anomaly detection results in a chart.', + }), + async isCompatible(context: EmbeddableApiContext) { + return Boolean(await parentApiIsCompatible(context.embeddable)); + }, + async execute(context) { + const presentationContainerParent = await parentApiIsCompatible(context.embeddable); + if (!presentationContainerParent) throw new IncompatibleActionError(); + + const [coreStart, pluginStart] = await getStartServices(); + try { + const { resolveEmbeddableAnomalyChartsUserInput } = await import( + '../embeddables/anomaly_charts/anomaly_charts_setup_flyout' + ); + const initialState = await resolveEmbeddableAnomalyChartsUserInput( + coreStart, + pluginStart, + context.embeddable, + context.embeddable.uuid + ); + + presentationContainerParent.addNewPanel({ + panelType: ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, + initialState, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + return Promise.reject(); + } + }, + }; +} diff --git a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx b/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx deleted file mode 100644 index 68d629e3f3ceb..0000000000000 --- a/x-pack/plugins/ml/public/ui_actions/edit_anomaly_charts_panel_action.tsx +++ /dev/null @@ -1,59 +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 type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; -import { ViewMode } from '@kbn/embeddable-plugin/public'; -import type { MlCoreSetup } from '../plugin'; -import type { EditAnomalyChartsPanelContext } from '../embeddables'; -import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; - -export const EDIT_ANOMALY_CHARTS_PANEL_ACTION = 'editAnomalyChartsPanelAction'; - -export function createEditAnomalyChartsPanelAction( - getStartServices: MlCoreSetup['getStartServices'] -): UiActionsActionDefinition { - return { - id: 'edit-anomaly-charts', - type: EDIT_ANOMALY_CHARTS_PANEL_ACTION, - getIconType(context): string { - return 'pencil'; - }, - getDisplayName: () => - i18n.translate('xpack.ml.actions.editAnomalyChartsTitle', { - defaultMessage: 'Edit anomaly charts', - }), - async execute({ embeddable }) { - if (!embeddable) { - throw new Error('Not possible to execute an action without the embeddable context'); - } - - const [coreStart, deps] = await getStartServices(); - - try { - const { resolveEmbeddableAnomalyChartsUserInput } = await import( - '../embeddables/anomaly_charts/anomaly_charts_setup_flyout' - ); - - const result = await resolveEmbeddableAnomalyChartsUserInput( - coreStart, - deps.data.dataViews, - embeddable.getInput() - ); - embeddable.updateInput(result); - } catch (e) { - return Promise.reject(); - } - }, - async isCompatible({ embeddable }) { - return ( - embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE && - embeddable.getInput().viewMode === ViewMode.EDIT - ); - }, - }; -} diff --git a/x-pack/plugins/ml/public/ui_actions/index.ts b/x-pack/plugins/ml/public/ui_actions/index.ts index 08fda084b413a..66dd1f0f06f34 100644 --- a/x-pack/plugins/ml/public/ui_actions/index.ts +++ b/x-pack/plugins/ml/public/ui_actions/index.ts @@ -15,7 +15,6 @@ import { createApplyInfluencerFiltersAction } from './apply_influencer_filters_a import { createApplyTimeRangeSelectionAction } from './apply_time_range_action'; import { createClearSelectionAction } from './clear_selection_action'; import { createAddSwimlanePanelAction } from './create_swim_lane'; -import { createEditAnomalyChartsPanelAction } from './edit_anomaly_charts_panel_action'; import { createAddSingleMetricViewerPanelAction } from './create_single_metric_viewer'; import { createCategorizationADJobAction, @@ -30,6 +29,7 @@ import { SWIM_LANE_SELECTION_TRIGGER, swimLaneSelectionTrigger, } from './triggers'; +import { createAddAnomalyChartsPanelAction } from './create_anomaly_chart'; export { APPLY_INFLUENCER_FILTERS_ACTION } from './apply_influencer_filters_action'; export { APPLY_TIME_RANGE_SELECTION_ACTION } from './apply_time_range_action'; export { OPEN_IN_ANOMALY_EXPLORER_ACTION } from './open_in_anomaly_explorer_action'; @@ -55,19 +55,22 @@ export function registerMlUiActions( const applyEntityFieldFilterAction = createApplyEntityFieldFiltersAction(core.getStartServices); const applyTimeRangeSelectionAction = createApplyTimeRangeSelectionAction(core.getStartServices); const clearSelectionAction = createClearSelectionAction(core.getStartServices); - const editExplorerPanelAction = createEditAnomalyChartsPanelAction(core.getStartServices); const visToAdJobAction = createVisToADJobAction(core.getStartServices); const categorizationADJobAction = createCategorizationADJobAction(core.getStartServices); + const addAnomalyChartsPanelAction = createAddAnomalyChartsPanelAction(core.getStartServices); + // Register actions uiActions.registerAction(applyEntityFieldFilterAction); uiActions.registerAction(applyTimeRangeSelectionAction); uiActions.registerAction(categorizationADJobAction); + uiActions.registerAction(addAnomalyChartsPanelAction); // Assign triggers uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSingleMetricViewerPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addSwimlanePanelAction); - uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editExplorerPanelAction); + uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAnomalyChartsPanelAction); + uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, openInExplorerAction); uiActions.attachAction(CONTEXT_MENU_TRIGGER, openInSingleMetricViewerAction.id); diff --git a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx index 426f6f32ee242..98a44c042db58 100644 --- a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx @@ -16,8 +16,8 @@ import type { SerializableRecord } from '@kbn/utility-types'; import { ML_APP_LOCATOR } from '../../common/constants/locator'; import type { ExplorerAppState } from '../../common/types/locator'; import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; +import type { AnomalyChartsApi, AnomalyChartsEmbeddableApi } from '../embeddables'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE } from '../embeddables'; -import type { AnomalyChartsEmbeddableApi } from '../embeddables/anomaly_charts/types'; import type { AnomalySwimLaneEmbeddableApi } from '../embeddables/anomaly_swimlane/types'; import { isSwimLaneEmbeddableContext } from '../embeddables/anomaly_swimlane/types'; import type { MlCoreSetup } from '../plugin'; @@ -41,9 +41,9 @@ export interface OpenInAnomalyExplorerAnomalyChartsActionContext extends Embedda export const OPEN_IN_ANOMALY_EXPLORER_ACTION = 'openInAnomalyExplorerAction'; -export function isAnomalyChartsEmbeddableContext( - arg: unknown -): arg is OpenInAnomalyExplorerAnomalyChartsActionContext { +export function isAnomalyChartsEmbeddableContext(arg: unknown): arg is { + embeddable: AnomalyChartsApi; +} { return ( isPopulatedObject(arg, ['embeddable']) && apiIsOfType(arg.embeddable, ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE) @@ -97,10 +97,12 @@ export function createOpenInExplorerAction( }); } else if (isAnomalyChartsEmbeddableContext(context)) { const { embeddable } = context; - const { jobIds, entityFields } = embeddable; + const { jobIds$, selectedEntities$ } = embeddable; + const jobIds = jobIds$?.getValue() ?? []; let mlExplorerFilter: ExplorerAppState['mlExplorerFilter'] | undefined; - const entityFieldsValue = entityFields.getValue(); + const entityFieldsValue = selectedEntities$?.getValue(); + if ( Array.isArray(entityFieldsValue) && entityFieldsValue.length === 1 && @@ -132,7 +134,7 @@ export function createOpenInExplorerAction( return locator.getUrl({ page: 'explorer', pageState: { - jobIds: jobIds.getValue(), + jobIds, timeRange: getEmbeddableTimeRange(embeddable), // @ts-ignore QueryDslQueryContainer is not compatible with SerializableRecord ...(mlExplorerFilter ? ({ mlExplorerFilter } as SerializableRecord) : {}), diff --git a/x-pack/plugins/ml/server/models/results_service/results_service.ts b/x-pack/plugins/ml/server/models/results_service/results_service.ts index aa0b075002367..0a732cf45a94c 100644 --- a/x-pack/plugins/ml/server/models/results_service/results_service.ts +++ b/x-pack/plugins/ml/server/models/results_service/results_service.ts @@ -157,7 +157,7 @@ export function resultsServiceProvider(mlClient: MlClient, client?: IScopedClust }); } - if (influencersFilterQuery !== undefined) { + if (influencersFilterQuery) { boolCriteria.push(influencersFilterQuery); } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index a762a716fa117..f73cbc46de222 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -25464,7 +25464,6 @@ "xpack.ml.actions.clearSelectionTitle": "Effacer la sélection", "xpack.ml.actions.createADJobFromLens": "Créer une tâche de détection des anomalies", "xpack.ml.actions.createADJobFromPatternAnalysis": "Créer une tâche de catégorisation et de détection des anomalies", - "xpack.ml.actions.editAnomalyChartsTitle": "Modifier les graphiques d'anomalies", "xpack.ml.actions.openInAnomalyExplorerTitle": "Ouvrir dans Anomaly Explorer", "xpack.ml.actions.runPatternAnalysis.description": "Déclenché lorsque l'utilisateur souhaite effectuer une analyse du modèle sur un champ.", "xpack.ml.actions.runPatternAnalysis.title": "Exécuter l'analyse du modèle", @@ -25673,7 +25672,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "Nombre maximal de séries à tracer", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "Titre du panneau", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "Annuler", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "Confirmer les configurations", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "Configuration des graphiques Anomaly Explorer", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "Anomaly Explorer (Explorateur d'anomalies)", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "Anomaly Explorer (Explorateur d'anomalies)", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9d39d1d2cea59..42ef3c2abf137 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -25437,7 +25437,6 @@ "xpack.ml.actions.clearSelectionTitle": "選択した項目をクリア", "xpack.ml.actions.createADJobFromLens": "異常検知ジョブの作成", "xpack.ml.actions.createADJobFromPatternAnalysis": "分類異常検知ジョブを作成", - "xpack.ml.actions.editAnomalyChartsTitle": "異常グラフを編集", "xpack.ml.actions.openInAnomalyExplorerTitle": "異常エクスプローラーで開く", "xpack.ml.actions.runPatternAnalysis.description": "ユーザーがフィールドに対してパターン分析を実行する場合にトリガーされます。", "xpack.ml.actions.runPatternAnalysis.title": "パターン分析を実行", @@ -25646,7 +25645,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "プロットする最大系列数", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "パネルタイトル", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "キャンセル", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "構成を確認", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "異常エクスプローラーグラフ構成", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "異常エクスプローラー", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "異常エクスプローラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 213e903c5dee2..d35e005f3bb61 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -25476,7 +25476,6 @@ "xpack.ml.actions.clearSelectionTitle": "清除所选内容", "xpack.ml.actions.createADJobFromLens": "创建异常检测作业", "xpack.ml.actions.createADJobFromPatternAnalysis": "创建归类异常检测作业", - "xpack.ml.actions.editAnomalyChartsTitle": "编辑异常图表", "xpack.ml.actions.openInAnomalyExplorerTitle": "在 Anomaly Explorer 中打开", "xpack.ml.actions.runPatternAnalysis.description": "在用户希望对字段运行模式分析时触发。", "xpack.ml.actions.runPatternAnalysis.title": "运行模式分析", @@ -25685,7 +25684,6 @@ "xpack.ml.anomalyChartsEmbeddable.maxSeriesToPlotLabel": "要绘制的最大序列数目", "xpack.ml.anomalyChartsEmbeddable.panelTitleLabel": "面板标题", "xpack.ml.anomalyChartsEmbeddable.setupModal.cancelButtonLabel": "取消", - "xpack.ml.anomalyChartsEmbeddable.setupModal.confirmButtonLabel": "确认配置", "xpack.ml.anomalyChartsEmbeddable.setupModal.title": "异常浏览器图表配置", "xpack.ml.anomalyDetection.anomalyExplorer.docTitle": "Anomaly Explorer", "xpack.ml.anomalyDetection.anomalyExplorerLabel": "Anomaly Explorer", diff --git a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts index 39a10547c57b9..69a95822be3af 100644 --- a/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts +++ b/x-pack/test/accessibility/apps/group3/ml_embeddables_in_dashboard.ts @@ -103,12 +103,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); it('can select jobs', async () => { - await ml.dashboardJobSelectionTable.setRowCheckboxState(testData.jobConfig.job_id, true); - await ml.dashboardJobSelectionTable.applyJobSelection(); + await ml.alerting.selectJobs([testData.jobConfig.job_id]); + await ml.alerting.assertJobSelection([testData.jobConfig.job_id]); + }); + + it('populates with default default info', async () => { await ml.dashboardEmbeddables.assertAnomalyChartsEmbeddableInitializerExists(); + await ml.dashboardEmbeddables.assertSelectMaxSeriesToPlotValue(6); await a11y.testAppSnapshot(); }); - it('create new anomaly charts panel', async () => { await ml.dashboardEmbeddables.clickInitializerConfirmButtonEnabled(); await ml.dashboardEmbeddables.assertDashboardPanelExists(testData.panelTitle); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts index 19b48858b115b..7f13c22c9eb91 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_integrations/anomaly_charts_dashboard_embeddables.ts @@ -73,10 +73,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { ML_EMBEDDABLE_TYPES.ANOMALY_CHARTS ); }); - it('can select jobs', async () => { - await ml.dashboardJobSelectionTable.setRowCheckboxState(testData.jobConfig.job_id, true); - await ml.dashboardJobSelectionTable.applyJobSelection(); + await ml.alerting.selectJobs([testData.jobConfig.job_id]); + await ml.alerting.assertJobSelection([testData.jobConfig.job_id]); + }); + + it('populates with default default info', async () => { await ml.dashboardEmbeddables.assertAnomalyChartsEmbeddableInitializerExists(); await ml.dashboardEmbeddables.assertSelectMaxSeriesToPlotValue(6); }); diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts index 549757883101f..c323356c73e6e 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_result_views/anomaly_explorer.ts @@ -7,7 +7,6 @@ import type { Job, Datafeed } from '@kbn/ml-plugin/common/types/anomaly_detection_jobs'; import type { AnomalySwimLaneEmbeddableState } from '@kbn/ml-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '@kbn/ml-plugin/public/embeddables'; import { stringHash } from '@kbn/ml-string-hash'; import type { FtrProviderContext } from '../../../ftr_provider_context'; import { USER } from '../../../services/ml/security_common'; @@ -521,7 +520,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const expectedAttachment = { jobIds: [testData.jobConfig.job_id], maxSeriesToPlot: 6, - } as AnomalyChartsEmbeddableInput; + }; // @ts-expect-error Setting id to be undefined here // since time range expected is of the chart plotEarliest/plotLatest, not of the global time range diff --git a/x-pack/test/functional/services/ml/cases.ts b/x-pack/test/functional/services/ml/cases.ts index 016ffcb8486de..6c481df8b99a8 100644 --- a/x-pack/test/functional/services/ml/cases.ts +++ b/x-pack/test/functional/services/ml/cases.ts @@ -7,7 +7,6 @@ import type { SwimlaneType } from '@kbn/ml-plugin/public/application/explorer/explorer_constants'; import type { AnomalySwimLaneEmbeddableState } from '@kbn/ml-plugin/public'; -import type { AnomalyChartsEmbeddableInput } from '@kbn/ml-plugin/public/embeddables'; import type { FtrProviderContext } from '../../ftr_provider_context'; import type { MlAnomalySwimLane } from './swim_lane'; import type { MlAnomalyCharts } from './anomaly_charts'; @@ -71,7 +70,7 @@ export function MachineLearningCasesProvider( async assertCaseWithAnomalyChartsAttachment( params: CaseParams, - attachment: AnomalyChartsEmbeddableInput, + attachment: { id?: string; jobIds: string[]; maxSeriesToPlot: number }, expectedChartsCount: number ) { await this.assertBasicCaseProps(params); diff --git a/x-pack/test/functional/services/ml/dashboard_embeddables.ts b/x-pack/test/functional/services/ml/dashboard_embeddables.ts index cf403bab147d8..9a5428276479e 100644 --- a/x-pack/test/functional/services/ml/dashboard_embeddables.ts +++ b/x-pack/test/functional/services/ml/dashboard_embeddables.ts @@ -117,19 +117,19 @@ export function MachineLearningDashboardEmbeddablesProvider( async openAnomalyJobSelectionFlyout( mlEmbeddableType: 'ml_anomaly_swimlane' | 'ml_anomaly_charts' | 'ml_single_metric_viewer' ) { + const name = { + ml_anomaly_swimlane: 'Anomaly swim lane', + ml_single_metric_viewer: 'Single metric viewer', + ml_anomaly_charts: 'Anomaly chart', + }; await retry.tryForTime(60 * 1000, async () => { await dashboardAddPanel.clickEditorMenuButton(); await testSubjects.existOrFail('dashboardEditorContextMenu', { timeout: 2000 }); await dashboardAddPanel.clickEmbeddableFactoryGroupButton('ml'); - if (mlEmbeddableType === 'ml_single_metric_viewer') { - await dashboardAddPanel.clickAddNewPanelFromUIActionLink('Single metric viewer'); - await testSubjects.existOrFail('mlAnomalyJobSelectionControls', { timeout: 2000 }); - } else { - await dashboardAddPanel.clickAddNewEmbeddableLink(mlEmbeddableType); - await mlDashboardJobSelectionTable.assertJobSelectionTableExists(); - } + await dashboardAddPanel.clickAddNewPanelFromUIActionLink(name[mlEmbeddableType]); + await testSubjects.existOrFail('mlAnomalyJobSelectionControls', { timeout: 2000 }); }); }, }; From 2b4a2eac0702c1a70b7306a7581f7799c9aae767 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 27 May 2024 01:27:52 -0400 Subject: [PATCH 02/59] [api-docs] 2024-05-27 Daily api_docs build (#184288) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/719 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/ai_assistant_management_selection.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/apm_data_access.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/assets_data_access.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/dataset_quality.mdx | 2 +- api_docs/deprecations_by_api.mdx | 6 +++--- api_docs/deprecations_by_plugin.mdx | 4 +--- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/discover_shared.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/elastic_assistant.mdx | 2 +- api_docs/embeddable.devdocs.json | 8 -------- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_annotation_listing.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.devdocs.json | 14 ++++++++++++++ api_docs/fleet.mdx | 4 ++-- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/ingest_pipelines.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_actions_types.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_log_pattern_analysis.mdx | 2 +- api_docs/kbn_aiops_log_rate_analysis.mdx | 2 +- .../kbn_alerting_api_integration_helpers.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerting_types.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- api_docs/kbn_analytics_collection_utils.mdx | 2 +- ...n_analytics_shippers_elastic_v3_browser.mdx | 2 +- ...bn_analytics_shippers_elastic_v3_common.mdx | 2 +- ...bn_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_data_view.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_bfetch_error.mdx | 2 +- api_docs/kbn_calculate_auto.mdx | 2 +- .../kbn_calculate_width_from_char_count.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mock.mdx | 2 +- api_docs/kbn_code_owners.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- ...ntent_management_tabbed_table_list_view.mdx | 2 +- .../kbn_content_management_table_list_view.mdx | 2 +- ...ntent_management_table_list_view_common.mdx | 2 +- ...ontent_management_table_list_view_table.mdx | 2 +- api_docs/kbn_content_management_utils.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ...n_core_custom_branding_browser_internal.mdx | 2 +- .../kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...bn_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- .../kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...re_elasticsearch_client_server_internal.mdx | 2 +- ..._core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- .../kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- api_docs/kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...core_execution_context_browser_internal.mdx | 2 +- ...bn_core_execution_context_browser_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_common.mdx | 2 +- api_docs/kbn_core_execution_context_server.mdx | 2 +- ..._core_execution_context_server_internal.mdx | 2 +- ...kbn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...ore_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...kbn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- api_docs/kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...bn_core_injected_metadata_browser_mocks.mdx | 2 +- .../kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...core_metrics_collectors_server_internal.mdx | 2 +- ...bn_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...kbn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- .../kbn_core_plugins_contracts_browser.mdx | 2 +- api_docs/kbn_core_plugins_contracts_server.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server.mdx | 2 +- ...kbn_core_saved_objects_api_server_mocks.mdx | 2 +- ...core_saved_objects_base_server_internal.mdx | 2 +- ...bn_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...kbn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ...d_objects_import_export_server_internal.mdx | 2 +- ...aved_objects_import_export_server_mocks.mdx | 2 +- ...saved_objects_migration_server_internal.mdx | 2 +- ...re_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- .../kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_security_browser.mdx | 2 +- .../kbn_core_security_browser_internal.mdx | 2 +- api_docs/kbn_core_security_browser_mocks.mdx | 2 +- api_docs/kbn_core_security_common.mdx | 2 +- api_docs/kbn_core_security_server.mdx | 2 +- api_docs/kbn_core_security_server_internal.mdx | 2 +- api_docs/kbn_core_security_server_mocks.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ..._core_test_helpers_deprecations_getters.mdx | 2 +- ...bn_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- .../kbn_core_test_helpers_model_versions.mdx | 2 +- ...bn_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_browser.mdx | 2 +- .../kbn_core_user_profile_browser_internal.mdx | 2 +- .../kbn_core_user_profile_browser_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_common.mdx | 2 +- api_docs/kbn_core_user_profile_server.mdx | 2 +- .../kbn_core_user_profile_server_internal.mdx | 2 +- .../kbn_core_user_profile_server_mocks.mdx | 2 +- api_docs/kbn_core_user_settings_server.mdx | 2 +- .../kbn_core_user_settings_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_custom_icons.mdx | 2 +- api_docs/kbn_custom_integrations.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_data_forge.mdx | 2 +- api_docs/kbn_data_service.mdx | 2 +- api_docs/kbn_data_stream_adapter.mdx | 2 +- api_docs/kbn_data_view_utils.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_deeplinks_analytics.mdx | 2 +- api_docs/kbn_deeplinks_devtools.mdx | 2 +- api_docs/kbn_deeplinks_fleet.mdx | 2 +- api_docs/kbn_deeplinks_management.mdx | 2 +- api_docs/kbn_deeplinks_ml.mdx | 2 +- api_docs/kbn_deeplinks_observability.mdx | 2 +- api_docs/kbn_deeplinks_search.mdx | 2 +- api_docs/kbn_deeplinks_security.mdx | 2 +- api_docs/kbn_deeplinks_shared.mdx | 2 +- api_docs/kbn_default_nav_analytics.mdx | 2 +- api_docs/kbn_default_nav_devtools.mdx | 2 +- api_docs/kbn_default_nav_management.mdx | 2 +- api_docs/kbn_default_nav_ml.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_discover_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_elastic_agent_utils.mdx | 2 +- api_docs/kbn_elastic_assistant.mdx | 2 +- api_docs/kbn_elastic_assistant_common.mdx | 2 +- api_docs/kbn_entities_schema.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_esql_ast.mdx | 2 +- api_docs/kbn_esql_utils.mdx | 2 +- ...n_esql_validation_autocomplete.devdocs.json | 18 ++++++++++++++++-- api_docs/kbn_esql_validation_autocomplete.mdx | 4 ++-- api_docs/kbn_event_annotation_common.mdx | 2 +- api_docs/kbn_event_annotation_components.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_field_utils.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_formatters.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- .../kbn_ftr_common_functional_ui_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_console_definitions.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_index_management.mdx | 2 +- api_docs/kbn_inference_integration_flyout.mdx | 2 +- api_docs/kbn_infra_forge.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_ipynb.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- .../kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_lens_embeddable_utils.mdx | 2 +- api_docs/kbn_lens_formula_docs.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_content_badge.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_management_cards_navigation.mdx | 2 +- .../kbn_management_settings_application.mdx | 2 +- ...ment_settings_components_field_category.mdx | 2 +- ...agement_settings_components_field_input.mdx | 2 +- ...anagement_settings_components_field_row.mdx | 2 +- ...kbn_management_settings_components_form.mdx | 2 +- ...bn_management_settings_field_definition.mdx | 2 +- .../kbn_management_settings_ids.devdocs.json | 12 ++++++------ api_docs/kbn_management_settings_ids.mdx | 2 +- ...bn_management_settings_section_registry.mdx | 2 +- api_docs/kbn_management_settings_types.mdx | 2 +- api_docs/kbn_management_settings_utilities.mdx | 2 +- api_docs/kbn_management_storybook_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_maps_vector_tile_utils.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_anomaly_utils.mdx | 2 +- api_docs/kbn_ml_cancellable_search.mdx | 2 +- api_docs/kbn_ml_category_validator.mdx | 2 +- api_docs/kbn_ml_chi2test.mdx | 2 +- api_docs/kbn_ml_data_frame_analytics_utils.mdx | 2 +- api_docs/kbn_ml_data_grid.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_date_utils.mdx | 2 +- api_docs/kbn_ml_error_utils.mdx | 2 +- api_docs/kbn_ml_in_memory_table.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_kibana_theme.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_runtime_field_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_time_buckets.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_ui_actions.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_mock_idp_utils.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- .../kbn_observability_alerting_test_data.mdx | 2 +- ...bility_get_padded_alert_time_range_util.mdx | 2 +- api_docs/kbn_openapi_bundler.mdx | 2 +- api_docs/kbn_openapi_generator.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_panel_loader.mdx | 2 +- ...n_performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_check.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_presentation_containers.mdx | 2 +- api_docs/kbn_presentation_publishing.mdx | 2 +- api_docs/kbn_profiling_utils.mdx | 2 +- api_docs/kbn_random_sampling.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_react_hooks.mdx | 2 +- api_docs/kbn_react_kibana_context_common.mdx | 2 +- api_docs/kbn_react_kibana_context_render.mdx | 2 +- api_docs/kbn_react_kibana_context_root.mdx | 2 +- api_docs/kbn_react_kibana_context_styled.mdx | 2 +- api_docs/kbn_react_kibana_context_theme.mdx | 2 +- api_docs/kbn_react_kibana_mount.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.mdx | 2 +- api_docs/kbn_reporting_csv_share_panel.mdx | 2 +- api_docs/kbn_reporting_export_types_csv.mdx | 2 +- .../kbn_reporting_export_types_csv_common.mdx | 2 +- api_docs/kbn_reporting_export_types_pdf.mdx | 2 +- .../kbn_reporting_export_types_pdf_common.mdx | 2 +- api_docs/kbn_reporting_export_types_png.mdx | 2 +- .../kbn_reporting_export_types_png_common.mdx | 2 +- api_docs/kbn_reporting_mocks_server.mdx | 2 +- api_docs/kbn_reporting_public.mdx | 2 +- api_docs/kbn_reporting_server.mdx | 2 +- api_docs/kbn_resizable_layout.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_router_to_openapispec.mdx | 2 +- api_docs/kbn_router_utils.mdx | 2 +- api_docs/kbn_rrule.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_search_api_panels.mdx | 2 +- api_docs/kbn_search_connectors.mdx | 2 +- api_docs/kbn_search_errors.mdx | 2 +- api_docs/kbn_search_index_documents.mdx | 2 +- api_docs/kbn_search_response_warnings.mdx | 2 +- api_docs/kbn_search_types.mdx | 2 +- api_docs/kbn_security_hardening.mdx | 2 +- api_docs/kbn_security_plugin_types_common.mdx | 2 +- api_docs/kbn_security_plugin_types_public.mdx | 2 +- api_docs/kbn_security_plugin_types_server.mdx | 2 +- api_docs/kbn_security_solution_features.mdx | 2 +- api_docs/kbn_security_solution_navigation.mdx | 2 +- api_docs/kbn_security_solution_side_nav.mdx | 2 +- .../kbn_security_solution_storybook_config.mdx | 2 +- api_docs/kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_data_table.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...uritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_grouping.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ...n_securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_serverless_common_settings.mdx | 2 +- .../kbn_serverless_observability_settings.mdx | 2 +- api_docs/kbn_serverless_project_switcher.mdx | 2 +- api_docs/kbn_serverless_search_settings.mdx | 2 +- api_docs/kbn_serverless_security_settings.mdx | 2 +- api_docs/kbn_serverless_storybook_config.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_chrome_navigation.mdx | 2 +- api_docs/kbn_shared_ux_error_boundary.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ..._shared_ux_page_analytics_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...kbn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...bn_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config.mdx | 2 +- ...kbn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...bn_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_tabbed_modal.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_solution_nav_oblt.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_predicates.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_eui_helpers.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_text_based_editor.mdx | 2 +- api_docs/kbn_timerange.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_triggers_actions_ui_types.mdx | 2 +- api_docs/kbn_try_in_console.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_unified_data_table.mdx | 2 +- api_docs/kbn_unified_doc_viewer.mdx | 2 +- api_docs/kbn_unified_field_list.mdx | 2 +- api_docs/kbn_unsaved_changes_badge.mdx | 2 +- api_docs/kbn_use_tracked_promise.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_visualization_ui_components.mdx | 2 +- api_docs/kbn_visualization_utils.mdx | 2 +- api_docs/kbn_xstate_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kbn_zod_helpers.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/links.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/logs_data_access.mdx | 2 +- api_docs/logs_explorer.mdx | 2 +- api_docs/logs_shared.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/metrics_data_access.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/mock_idp_plugin.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/no_data_page.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/observability_a_i_assistant.mdx | 2 +- api_docs/observability_a_i_assistant_app.mdx | 2 +- .../observability_ai_assistant_management.mdx | 2 +- api_docs/observability_logs_explorer.mdx | 2 +- api_docs/observability_onboarding.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/painless_lab.mdx | 2 +- api_docs/plugin_directory.mdx | 8 ++++---- api_docs/presentation_panel.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/profiling_data_access.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/search_connectors.mdx | 2 +- api_docs/search_notebooks.mdx | 2 +- api_docs/search_playground.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/security_solution_ess.mdx | 2 +- api_docs/security_solution_serverless.mdx | 2 +- api_docs/serverless.mdx | 2 +- api_docs/serverless_observability.mdx | 2 +- api_docs/serverless_search.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/slo.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/text_based_languages.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_doc_viewer.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/uptime.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 695 files changed, 734 insertions(+), 716 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index a8ff588b79080..007fa5b67a6fa 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index abd046bcf69ae..9e58f3b8d8535 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index 1723ebd4ec7d8..c45287e0b0658 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 8d6fc993bda14..73450a7f78f8c 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index eeca7cd5e0d62..fda6ef4f66870 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 9114300725d2b..a7527141ff849 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 11cc1236055e8..f815b118d4a1d 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 2a4ba627e7192..922f389398745 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index 23fc89452b55b..678a225953251 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index 685975a75bbcd..cf225ce504bb5 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index be34ee0630279..6bc884d785244 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 8f3e8a7e491f5..2339e6488e252 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index b80a9cc36395d..e4365deef8c86 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 22733b389aada..adcfdad03b535 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 89e65a5b24c25..c5c1b875ab500 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index c2daae947a252..bd35cbf44cb50 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index c730ea8fa0114..fee53fa5b9556 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index ff5b4d464bb5d..72778b64abe5f 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 6c8127935cfe2..037be50397b62 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 0eb5d6efff707..599d7a877df57 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index e9a1cfee92e8b..d12a9000ac657 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index f3c433f0df8a2..24494121d6288 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index cb48517c41f97..371d7024c9f99 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index c7cdd22a8702d..99879301b021a 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index d0dc7afb1e6c0..43639daee3862 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 16c9a91990ed0..19f24774ec3bd 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index 967f035932699..ba13c86b8fdbb 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 50f5ea6137513..1653379b77ea4 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 9b99e4b24bcd5..faf669346403b 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index d20d90251a8e0..a7d95b6abb2dc 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 3ea3deb355dce..a30c351e121df 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index a15efa25849f9..28f607334b6b4 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 4472313f06924..4c297d5fa47d7 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index ccdd467fda0f9..ad3c71b1799e5 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 4abe92cc80da2..723ac413ace29 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -18,8 +18,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | ml, stackAlerts | - | | | data, @kbn/search-errors, savedObjectsManagement, unifiedSearch, @kbn/unified-field-list, lens, controls, triggersActionsUi, dataVisualizer, canvas, presentationUtil, logsShared, fleet, ml, @kbn/ml-data-view-utils, enterpriseSearch, graph, visTypeTimeseries, @kbn/lens-embeddable-utils, exploratoryView, stackAlerts, infra, timelines, securitySolution, transform, upgradeAssistant, uptime, ux, maps, dataViewManagement, eventAnnotationListing, inputControlVis, visDefaultEditor, visTypeTimelion, visTypeVega | - | -| | visualizations, lens, controls, dashboard, maps, discover, ml, infra, profiling, slo, links | - | -| | lens, controls, dashboard, observabilityShared, ml | - | | | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | actions, savedObjectsTagging, ml, enterpriseSearch | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core, savedObjects, visualizations, aiops, dataVisualizer, ml, dashboardEnhanced, graph, lens, securitySolution, eventAnnotation, @kbn/core-saved-objects-browser-mocks | - | @@ -61,6 +59,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | @kbn/monaco, securitySolution | - | | | fleet, cloudSecurityPosture, exploratoryView, osquery, synthetics | - | +| | visualizations, lens, controls, dashboard, maps, discover, infra, profiling, slo, links | - | | | actions, alerting | - | | | discover, @kbn/reporting-public | - | | | data, discover, imageEmbeddable, embeddable | - | @@ -106,6 +105,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | visualizations, graph | - | | | kubernetesSecurity, osquery, threatIntelligence | - | | | @kbn/core, lens, savedObjects | - | +| | lens, controls, dashboard, observabilityShared | - | | | dashboard, canvas | - | | | dashboard | - | | | embeddable, dashboard | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 94a7166973c1d..6f4c1dea939ff 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -1028,8 +1028,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | [register_ml_alerts.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/alerting/register_ml_alerts.ts#:~:text=registerNavigation) | - | | | [job_creator.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts#:~:text=title), [categorization_examples_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts#:~:text=title), [configuration_step_details.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_details.tsx#:~:text=title), [data_loader.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/datavisualizer/index_based/data_loader/data_loader.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [use_index_data.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts#:~:text=title), [configuration_step_form.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx#:~:text=title), [configuration_step_form.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx#:~:text=title)+ 10 more | - | -| | [index.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/index.ts#:~:text=registerEmbeddableFactory) | - | -| | [get_embeddable_component.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx#:~:text=getEmbeddableFactory) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/plugin.ts#:~:text=license%24) | 8.8.0 | | | [annotations.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/ml/server/routes/annotations.ts#:~:text=authc) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 7763384170ba7..edf72c51f521e 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 8700ada6e3d09..37967262f1944 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 9470b077bc0c1..d44a5dc8a5e22 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 5f6ede2706f29..6339fb3b2b01b 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index daa23f3e03003..c66ec0daf4431 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index f5f1b9b6abe70..9e970566e7c12 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 7df6e9bf552a7..7316bdb6b5955 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index 1322b23c1e5da..3398442983e06 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -14210,10 +14210,6 @@ "plugin": "discover", "path": "src/plugins/discover/public/plugin.tsx" }, - { - "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/index.ts" - }, { "plugin": "infra", "path": "x-pack/plugins/observability_solution/infra/public/plugin.ts" @@ -14640,10 +14636,6 @@ "plugin": "observabilityShared", "path": "x-pack/plugins/observability_solution/observability_shared/public/components/profiling/embeddables/embeddable_profiling_search_bar.tsx" }, - { - "plugin": "ml", - "path": "x-pack/plugins/ml/public/embeddables/get_embeddable_component.tsx" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/services/embeddable/embeddable.stub.ts" diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 0f03975d8dc82..7ed91529a9fb8 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 307ba79aabcc9..7a17f34c192a6 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 9ce88bb1ab916..f38506783d091 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index ebb4f006ace86..b53756b6008e7 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 8c07efa929150..5626378a0f6c0 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 6542fb00edc6c..0cf2f1ba5c272 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 182a708e2cba8..db7c297385ae5 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index c6a31ce67126a..a2de0179deafa 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index ee2d96da709cd..a36d3eeacfda9 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index a470059efb14c..c9232260fef76 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index e94808f2707a0..34d07a2d17dd4 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 54c6029b18238..830c5a2876a8d 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 20087eed7766c..7b85d1bef2ab4 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 6c06943fc0174..e3801b2775ef3 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index d1c70f7c0a86f..8094001ef9db8 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 870de6489d9c9..8470505020506 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index b6f87bd3764b1..88f1d1fbfe2ab 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 66aceaff9ae53..a5f3ff34bfd8d 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 8dc8d7853da44..599b36ce47d96 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index c1f1448bad18f..be3151b23af8e 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 8021fa3872b04..59504e88c143c 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index cd9e2a2e5d231..830cc2d6d3bac 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 50acf5f61871d..a7fdfd213a205 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 7fb9aa8a7a3d3..5fe0cba31cc51 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 9148c75a00144..76e4c37f5e6f2 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index ea6ee9e25f18a..55e2afac5e1c5 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index fb8d0d61c4353..cb861226dee24 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 1f53d43ae4137..1597e582d0cbb 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index cc485026a9c3e..e7987f0be986b 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -25114,6 +25114,20 @@ "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.RegistryVarsEntry.RegistryVarsEntryKeys.hide_in_deployment_modes", + "type": "Array", + "tags": [], + "label": "[RegistryVarsEntryKeys.hide_in_deployment_modes]", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/epm.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 79dedb3ee428d..ab89a00834c7a 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1309 | 5 | 1188 | 66 | +| 1310 | 5 | 1189 | 66 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 027d172fdc785..b6fa38c321d9d 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 79dcb830cb11e..75e493c31a6dc 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index 8ba7fd344cd22..ea1e067210f4b 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index b78258826330d..8e56a1eb40b8c 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 41c8309475ce7..9def7c2abdc12 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 9802f2a41ef1d..c164b68269a3a 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 8153bc2cae01c..8179231734ac8 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 42f3e51e3ae35..16d21094deb97 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 5d4227bb0de27..e9d552e3f9ea2 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index efc7a494887ef..a35fd8f1b2cee 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 668f038f6e8cb..7090c08d86c7a 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index fafd6da65d8a3..01e426156b2c5 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index ca98b2af38a19..18e5c632f1bef 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index c06f5fbfddbfe..4c5b01052f3b6 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index a1f912bb205e3..4cc6d2ad7ea26 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 9efbfcdb895e1..2a27a5c94ea9b 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index a2130b301d857..d5e350b203769 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 86ea6492e3147..4ae44b98ad151 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 62c6a8839ccaa..962877f5cb324 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index aa94ecee7227e..6dadceb702c09 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 99b6fabf0450c..6e637c7fd498f 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index dc90fa2f92e5c..6777b705fb00c 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 04f24df27c3bd..82de6cf28a316 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 9286d5486fcaf..17d590e5edcc6 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 1ee7e47d88dd2..c8d3cf6238eb3 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 14816f0aad9e3..146c5d74dbcb9 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index fb20061c3457d..8d56793fdf3ed 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 11d510821d550..2bf4553c8aaa3 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index babf1f0f51f7f..65fa823226173 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index b475cdcabd2fd..83a1036e3a31e 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 8ecc7ab54b1b6..8e28aa8b3bd8f 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 621e99ccc1ff3..bc3c68982fe36 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index a189e52483f24..cd92084b37a21 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index f93ca4aa972e1..1a17b58820b19 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 554cedb736e31..9ceb4537545fd 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 2704629372fbc..2da21884a39c9 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index ce0e13e859e5b..1f8628c10a066 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index cb5980b048704..c96953e6b0477 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 2f344658ac3c6..b9334e11202ae 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 034fd4f8b1f2c..25ef89ab2129e 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 68fd997506364..472468d9658ec 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 000f8704c768a..96420b091b3d6 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 81b8f5bced327..dbed3a1595621 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 3c0f9d26e267d..ba7c61490ea79 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 351e211e77c3b..98e11d4734e82 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 5dc9de9c8a32c..79ac063016227 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index deeca11fbacf1..ee8135c3c5272 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index c17d47e83490d..cbc6b269aadf6 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 6f9b9d080ce38..bf17c59ff2f8c 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index f330c0aea3b2c..f278a2cd42c42 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 4da893732c1ac..da9c328b06eb5 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 5780993f9b6d4..6499deac7ed4d 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index bba48c92f6e4e..599e54a370d65 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index a16bd6c42222d..37c3c5df8f407 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index fe538f6d9cef8..1b1f74bb8ccbf 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 46aacf54d6be3..47def5616d9be 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 6647818ed3641..f829fa0be1685 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 580e25611feb9..a0fac27d2f86e 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 2319e64003105..544cbe863444c 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index d91f16a400267..907fc10aa4b4b 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index f4ccff13933dc..cbde5dd789b59 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 4b70b54cdf6ee..bb3f83eec6d07 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 3f8f143f9f20f..f64d59412936c 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index b8207bcc3b201..6d621b5d04e62 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 61e49c0c5dc94..00374fe6690e3 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index c59e682b07f0f..1152589c3b86e 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 072535098bd6f..6c909437eebb9 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index f66265afc6b43..001ab6bf40bea 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 909676bed50da..084abac29ca1e 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 700a9d15ad2ff..fdd05785ce775 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 9abc4850a5606..9d358ca5f2de9 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 1626885b30fd5..f36433d2e9c5b 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index a26682ad0cd7b..0e57ff263dbae 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 7517e2f8a7103..20f5a40418b0f 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index aef3b62400f77..c261d79bf2d73 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 05e14961ed459..838e3f663da1b 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index c615cd0ca2eb4..38ea045ca2dd7 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index ebb4a7419971a..1816c02361a22 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 6db6f1c107d7b..621c355a141bc 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index 8e6df37ac19dc..b6d418c2e0628 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 4ecc6dbae3096..1042ef8b2c958 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 0f115a7445ba7..0d71d0d363465 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 9dc7879b039b6..233a7fd8c191f 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 5fedd7d2e3e57..e05dfb9341922 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 634b80ac14785..b763a6279b2f1 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index e5857583c8e0e..080c0eb9bca6b 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 506260dc916f4..5f58987c9ccd0 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 73a4d43652894..cc6176030089a 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 9870a636432e0..99363101902db 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index e07c71e9fc17e..a467363ab63bd 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index dc57562f00e24..24497190a6ff5 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index f905a64f99f5a..97c2849499dcc 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 33a7e122ad9bf..7d1c1bf0b3ec5 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index d1affa7bad6b7..4d42e35533dc6 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index f1c9fe28bae6a..1a7175df0ce14 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 7d875dcbd9a91..233713edd0555 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 7e2990bb8af2d..27a78aa4ea953 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 27ffa9ef4f07b..377faefc70428 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 15f5473fdd3fd..d42df4d9dccba 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index aeee55b47bb4c..42ffc6f13c3fa 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index f08fd4db352e1..f1cc9527856c1 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index e6b28c433b143..ad7935401bdb0 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 96b4e15963e3c..6ac69dc82b5d3 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 903caff878113..d6346e04bbca5 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 8eaa477b38269..d5f16431b874f 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 67943d3220a3b..7b76f87033aa1 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 93994d6ba4a48..48bcc1ca9dc0a 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 70a4cbc2bb5c5..082a4a71f60dc 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 246ff76ea2081..be5564af87cad 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 9dd4c242b682b..114a0ff780a59 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 658f5c2012b50..c93b9631ea139 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index 3b2a4e84d9a23..cb4d4f7c2a654 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 45dea9af5ef19..6803d420afeaf 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index f2ab0d311c1e7..c0b42689bd572 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 38a44632bd699..ce30ce160f9a9 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 9387238ee777b..b2fe36ef699da 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 29c0c7b25bbe4..8cc7c20f0f3c0 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 058f48d70b65d..89e7c0186b20c 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index cde449b27df85..edb2a003f0a2b 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 9d14aaaef2f6d..18d8da8245fb1 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index 89cff734a61c7..abe998d23c7aa 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index c690cc8d0066f..0aea1e7cf2b3f 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index d988350d76af2..fb135cc13c0ed 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index dbb6c83e22af6..f72961f474669 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index b2280925e61c8..4be364df62491 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 0f2b5014ebfbf..dfe6bea635234 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 66873d96ba67d..9fc740f7ac77e 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 919690a73c905..1f35eccd8de76 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index b91a8d0b2e51c..98e8799a39787 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index db7f47c50fecc..5b6437a31ff80 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index e2f19cc0e1695..6042fac5ab04d 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index e71a493e54c21..99d39ebbb6cc2 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index b93b9bd01aab6..0a4e804eb98e3 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index b4f3cbe416c20..71c0f27c536ad 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 809b786f3b099..92f83cd068196 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index c95d628631b4e..58b385afb584e 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index 99fe7f480f469..b5538a6417a82 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index 0acb5fa66878b..c58bbf1350b6a 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 5205f5723eded..17804a2a2932b 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index cafd27968b47c..7d7b40d66f4c8 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index ee3a3ef922889..6d1b9dccbcf31 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 47afedb514a44..d00376abf75ca 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 39e02eaec4e28..654836432d519 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index d09b2c9607e90..79e704bb8aaad 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index f20ae899ae115..7b1e7de9325ff 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index bf42fdb880e92..595d0a2300507 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 033840ccaf223..db670ecf777c9 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 1fa374ab67fa6..93a8b10e916ca 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index c708dff320ee0..c8c86bd76f134 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index cab71dee4f10a..21fd745e9c761 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index b6391fda8d90b..d22fb294f037e 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 215cf72b17249..6f1fd98b3e2ea 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 2dd46e8075f3e..9cdb53cc04ad0 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index c4f554dc70e36..b65b4175560d9 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 8858d1dcaaae9..6231c464fe4cf 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index cee49ecc81b18..f656575427911 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index 615e68ba54f44..fe9148f900292 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 040f88ba20e87..d327bd9459b03 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 237abe52549bc..6e73b6ca6fc11 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 113645d1d5adb..362c8d8b94a8a 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 840c3a551a3d6..4de1a39d34bc0 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index cbab3add667fb..34bcb40f6464a 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 99fb0a7c82fd1..cc4dc32a67311 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index d44f7fdb8e0ed..63d1d05339ab9 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 15203507883b8..8dfa8e576931c 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 881809caceabd..8a83a877097d1 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 8279126418348..7993fb3b05d27 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index ebfd1fed3cab6..fb16e9ae363c1 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 334685d46009e..4813d017b13d6 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index af6cf50f80583..95ba9aef4d19b 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 7ba99832890a6..17524185a8bbf 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 2337263f039af..c2189782caae9 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index eb4d4e5e30b8a..3bd8c5863eb7c 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 582a1f1bbb324..b44d92e1e61d0 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 8cb8cc1083154..9cf3f26c110ef 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 7fa566a6bc5c2..49a79449cc5d9 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index bdfeff8968db8..dcb8eaeea2d8e 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 4e9ed55bf5dde..512ab251f55cc 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index bdb3cde6e95ca..03055eead866e 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 0a8106649b7f4..62d897062a66e 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index c75d1534d77a7..906bb1847b9ba 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index c6a4bac606d53..cfbd9d6c7f399 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 43a64a3b8edbf..63dd24e48669c 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 58d157ca34e2a..0381032011a6b 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index ae1b24ce3185d..13cd3db1ca3e0 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index 5004dc1630242..e8a146fabe03c 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 3adbcde8689df..34decc2c10999 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 41ce61850472c..7aa3bf7b7bd14 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index 59e828b84221a..c37e1125563f1 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index d27fba8941cd1..fc57e6f6f1267 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 484f1cc9044fd..9b2160767157d 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index afab3cec5b44d..c9e5e31a97135 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 24f255c1b8fb6..14f00d5d6fe50 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 9caf0af1fa815..525794f559e16 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index ed80acfc298a2..6d7ec3d7d8e93 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 76f43f1bbec15..94fd5961d50d7 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index 152e83d41617c..d63d58a224dbf 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index b35f833d7cb1d..d5131ea030434 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index ecadb62630b4c..f3332cc7f9f30 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index 40dbe315eb605..91f95b8105712 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index f318280858453..334788c03fcd7 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 01f361fc3f45e..3c411e4e0c948 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index eed6e28929e8a..361d6f8deeea6 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index a0554906da7b3..b932d4dbd6bf0 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 8842ee450ac86..3c19c0723ec4f 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index df902033b253d..f4ae005f6a14a 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index ea360da7fea07..e2483e1a446e1 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index d4ebaa5055459..b3b3d6444879d 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 5f1705cfb81ec..6534e027bbac2 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 71e27678c1b6b..30a45d3fa1d43 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index f85d0acd908ab..cdcdfd085da8b 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 6736e77002911..263cc553262cc 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 396c012dd7bb6..0be6298718c6f 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 3ed1e3f60ca3b..a3ae704f58fdd 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 8313566bd60c1..008865cf75451 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index c8727822cac46..8d8394ec3d41a 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index ad6d7947fa44f..9d4eb0002ef39 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 39e96506eff7f..73178642f1051 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index dcdcfcbac3a45..67fd2a9a938e3 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 0ee933bf08e9e..48980ac73b4e2 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index 1ebbc45a16cd9..cc0b7d081ffae 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 6bdce20ec4569..0b89752421732 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index 9a8c2469367b2..ae8b77ad7b193 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 672b82605c7f3..657f320c1100b 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index 3b47905618f9a..b5b6f57f488f8 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 685087c866ac0..1b76a1eb3d879 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 2f5d5d868e78e..399c457fd4787 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 9feb4db9089a2..38787ae7fa0d5 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index 6cd36c4a4e590..f539eee5f78a9 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 5c102083835e8..1556e68abb099 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 3f972ca464511..4a47c0ef80838 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index 5bc3ae6210cc6..dff29f6b0f077 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index cee0f7c998215..c7a94ef3dda45 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index fd5777f4625b4..69c0f2528adce 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index ac67687e05717..94e0841b320f3 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index e8a59e0630244..2215254526bcb 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index c4af9074bcd95..51eabe740c3c1 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 2f65f4412f9cd..1b61b79dccc93 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index e8ca952c1c61d..a777b53158e49 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 36144d2ae2374..d17394158986b 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index eb56875bf539c..949fa99745ca9 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index 151f7616afa67..eb1fe118432ed 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 73b414f99d802..64d6ed9f1f825 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index cc0d5c6e96b7e..ad4dda37aabf7 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index c4c27bece7bff..b85bb3ca1b665 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index dfae276b32bdb..82a96981eb133 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index 995b83f949ee1..cc17543cba74c 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 79cc3b9e3a5c5..6347f7ff9d568 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index b769d08ec8917..7156db6a7d083 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index fa2f1667f9ab7..c4f21a9ae9e37 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 7e6b0c6aceef5..799125d79f534 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 37de496d34fea..9ac2897d8114b 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 8c8ca98afc88e..3bfb374499fb1 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index ed327603aa451..b89028775a2af 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 1f51a030e621f..e0130fdbc9770 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 6893f75315f30..ca3c40d95674b 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index ba2e201835e4a..4d7f26b85b885 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 1280623574e70..36209aad520e3 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index a2f7e2d7114f6..1884280fea8a3 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index e0a8b790a75cb..0ef8e1895ac02 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index e390f2fa93898..6eaebcae78676 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index 2875edb89ebb9..ed87b9eba83d8 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index 09a97fc321257..dfeaf1e1bd0aa 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 4398c2800491b..5781af4f1bfb0 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 74a9d332fdcfc..8ae742997ea71 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index c574bf1c4c09a..faf0d90f30b04 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 6a10d6e71589b..fbd0fa95ea454 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index b765f55d12d20..e8dcd5e568b00 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 7300d6d64697e..f6b14cea32796 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 0ee8c1a7f7cfa..9fdc2b1161d0e 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 7d21cddb833e3..91572c451be33 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 6c0d4507e809b..a6fc22f62d479 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index a509c6a9c3b4c..870a7738e7f9c 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 2f972d0221272..578cc2faecd12 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; diff --git a/api_docs/kbn_esql_validation_autocomplete.devdocs.json b/api_docs/kbn_esql_validation_autocomplete.devdocs.json index 40625e66ca85d..b97b81a2eb1f5 100644 --- a/api_docs/kbn_esql_validation_autocomplete.devdocs.json +++ b/api_docs/kbn_esql_validation_autocomplete.devdocs.json @@ -1186,7 +1186,7 @@ "section": "def-common.FunctionDefinition", "text": "FunctionDefinition" }, - ", { withTypes }: { withTypes: boolean; }) => { declaration: string; examples: string[] | undefined; }[]" + ", { withTypes }: { withTypes: boolean; }) => { declaration: string; }[]" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts", "deprecated": false, @@ -3235,7 +3235,21 @@ "FunctionParameterType", "; optional?: boolean | undefined; noNestingFunctions?: boolean | undefined; supportsWildcard?: boolean | undefined; constantOnly?: boolean | undefined; literalOptions?: string[] | undefined; literalSuggestions?: string[] | undefined; }[]; minParams?: number | undefined; returnType: ", "FunctionReturnType", - "; examples?: string[] | undefined; }[]" + "; }[]" + ], + "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/esql-validation-autocomplete", + "id": "def-common.FunctionDefinition.examples", + "type": "Array", + "tags": [], + "label": "examples", + "description": [], + "signature": [ + "string[] | undefined" ], "path": "packages/kbn-esql-validation-autocomplete/src/definitions/types.ts", "deprecated": false, diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index c97ed9f64cb63..49458cdc03db0 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 193 | 0 | 183 | 10 | +| 194 | 0 | 184 | 10 | ## Common diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 53a3c58c6bd0f..e6baa12440493 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 69b2a23d70f53..e57a5ecb345e0 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index f0c091e0cc7d8..6692ba53f6458 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index e53515dc66375..08c8e98066845 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index bd5f88ec95de3..872156c797e8c 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 7282b9b37e829..588c36c87332d 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index 5dd8c2c76fa09..c86e56de64496 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index a5859e459f219..559d47c01a7de 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index 779c8c4da71d8..f422587ecc2dc 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 3987841762111..10b2985621e19 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index c7fa5d6528f98..6c81ab9dec28d 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 15c0695889139..416f4991ad09f 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 3f0375b37859a..aa8deafe55c1c 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 21c81a81c8787..213757d025450 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 3759c8b702e09..f26f2cecad027 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index 4dd24a8154425..b3b2722cec2df 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 18e0ede1b58a6..61f9840bfd9e0 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index a37376d6bf80d..64859d6488416 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index 63101a6c97d06..ee6db5ed7dffd 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index f35bf61546e58..51c27347f4852 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index ed6f26bca7d75..436c5777e9416 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index d3eeddca58b6c..7912dae2fdf46 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index cd4332ba3991e..19caca0579a3a 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index 1aa7b429a690e..a1c174987e3b2 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index f43cd69b05459..0ef231bee51a4 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 6e0f088f1ce3d..452040267cebb 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index 70f8b401985ed..b71c9ff4837c0 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index e9dcf100c3983..fa6e70ac1ed93 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 82758f975d7b6..f2cb48a3627b8 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index f7869ab4f77e7..6742d26dcae45 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 5295d7a8f15a8..7ade04d52e4a8 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index 4a329327ef98f..ad5c3f6f37a40 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index b3eff5d6863ff..9ddcbf8ad3ae0 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index bd89d7e082fce..2bc8efd61c186 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 7b672d039f3da..c16356e58d73e 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 9b386e9285e00..45c54d6ce8f29 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index cb3510007411f..bf584ad706562 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index e3376c226ba73..54ecdda1db2bc 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 8eae7c73cb947..8919d0610f2d6 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 36eddd166218f..18ba80d52a1d8 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index d0fff926be690..6cdd3754eb575 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 6ef66604120dd..f06b04c9b4e9f 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 5fdf1c8c8387c..5182b52733766 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 8b750ccf023e4..1d1333c514069 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 7f82f349df177..a6c57d2679c20 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.devdocs.json b/api_docs/kbn_management_settings_ids.devdocs.json index b4b24972b6989..5ddb78e79b853 100644 --- a/api_docs/kbn_management_settings_ids.devdocs.json +++ b/api_docs/kbn_management_settings_ids.devdocs.json @@ -1344,13 +1344,13 @@ }, { "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", "type": "string", "tags": [], - "label": "OBSERVABILITY_ENABLE_CONTAINER_ASSET_VIEW_ID", + "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", "description": [], "signature": [ - "\"observability:enableContainerAssetView\"" + "\"observability:enableInfrastructureAssetCustomDashboards\"" ], "path": "packages/kbn-management/settings/setting_ids/index.ts", "deprecated": false, @@ -1359,13 +1359,13 @@ }, { "parentPluginId": "@kbn/management-settings-ids", - "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", + "id": "def-common.OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID", "type": "string", "tags": [], - "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_ASSET_CUSTOM_DASHBOARDS_ID", + "label": "OBSERVABILITY_ENABLE_INFRASTRUCTURE_CONTAINER_ASSET_VIEW_ID", "description": [], "signature": [ - "\"observability:enableInfrastructureAssetCustomDashboards\"" + "\"observability:enableInfrastructureContainerAssetView\"" ], "path": "packages/kbn-management/settings/setting_ids/index.ts", "deprecated": false, diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 8e698f93fea85..1aa2558466b9a 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index db0d52392d324..0216db77e2541 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index dd737d1ba4e2b..873cb21d87155 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index f143006787e7b..6d095efbd14a9 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index aceb187e7c749..c82232ea18aed 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index f2ee9c2206f64..6746e2168c713 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 3868e2b15e84f..00dc196b612a3 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index f93a99a72b11a..216c680d5bc92 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 3936d3bc3f8eb..655addfc327e0 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 2d600b386a2c7..9279be7cab54c 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 557f83248daeb..54e147e71173f 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index 013e5a4609c1a..ac644f28f9944 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index dc61ea93474a6..0e06489bcb017 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 58a6d67dd6814..a64750bfcf07c 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 436ab3d3e4d86..815e2df99d117 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index b2c686428fff0..3d8db9c9a8d25 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index 0862edca82983..b9314e19932e7 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 04ab38b1aa87a..f588ddfe36832 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index 5d6393aa3d057..d29b329188f87 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index a55ac3dceb139..57ea2a9b30780 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 74a5b6db28d19..2af6d1e30429b 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index e8ff7e9b17207..ca9dc59936c5c 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 7d9ddb0c9dc28..bb78688e3f9ca 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index a93deac7f4480..b47e3f81b3942 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 0c60d620c0f39..44e36bf9c1ab4 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index c89a54e896a61..8057880e30c40 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 7002a9674ccd1..a75a4e96bf760 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index f0caf7de0317e..9782e3719d0af 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index 021e1fed1372b..d1d695e8a2383 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 3ca76c27a90f7..00c99dfa99e31 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 0d9b585620ae1..d24c763ea11f5 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 07c47c6e1189b..708f306c26a6b 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index a7fe767d8bce3..40a49a16d0e32 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 3c0aeaae4800e..1210d4b177616 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index bba132cdad00c..b2e615c757bec 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index d519d1f1c4a7c..95c4b45e27b1c 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index fa9fdc6f53147..edab6c2f8d45d 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 06d0e9451f3ff..558863972f7f0 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index 9f80b2c109cae..dfd1bc0512ab0 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index bf22b60086162..b99156ac0b702 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index 43ebba64d53b1..f14af3c059fde 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 9daaeb6ce8263..3002cf339f020 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index be1887dce41ba..ac0621496a6f6 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index c3fe7661261d1..5e97b299897ba 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index d63b39598801e..e7209b93128f2 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 808672ea8f429..0afee9083ab2d 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 7b74faf36e701..297974d343a7d 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 017503c5efd9c..7485ace1eda6c 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index c3faae2d8845a..ace45ffb489ae 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index 3baf7f185582d..f5cf0dc3a1f21 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index aebc887cccabd..ceebc7ac83580 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 8a2e3a0b5f8cf..956eb75e30be9 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 359e98f0d3f0a..7ae20499de1fd 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index e3d7756cf1855..4b25ff900a1f1 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 9b43a4b4f5d39..654acf461028e 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 492e042bdfb94..6454ca4440b14 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 6c5a6241435bf..f2cba0212429f 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index 00f3e97c7c3b2..a2b525b42bcd0 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 3c0dcc88052f3..81e850e7c72df 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 8324881a57ffc..c54a570c9ddfc 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 85857b57c5957..55af9cc7c5c47 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 0ef923c25b393..9d06b02bebcbc 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index e2feb0ccbf73c..434ca8a520038 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index a5d401a48dfaf..3d08bcb10c89d 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 9c3551591fc3f..8a720c0082e0d 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index c6dec769f0afb..8f1bd336fb910 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index 02bac16fcf7c4..c8c78efb5c1e2 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index e0c4acedcbab4..cfa6d6da96cc8 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 5b90ea32e3811..2780da85adab0 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 7dde00ad16766..fb8a6d9e62425 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index 27d583248839b..b4f8f381125c4 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 8324f251c3a20..9aa265a90fa85 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index fb39628d4a067..c203c52249a07 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index d1e92a71ec254..dd72fd6ca1878 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 93b0619d12d22..4cfbd615316be 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 88c885e867e31..7f42011d2aa0c 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 0b8d242f5fc1c..4a7981635308d 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 95737aa864f53..38346a4e39963 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index cc250e27033a9..300d466e7eea2 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index 5220c45eb4ebb..f5c6b3bb0ef50 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index b551ad2a20c3d..4feadb9dbcfbf 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index b34232c13b5f8..a451e46369f7d 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index c49f111942187..99ad978bb1f77 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index e69df56d20bab..d15c94b3f9364 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 30d0ea8326cac..a98862d2203ef 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index 33f103ea761ee..c7821604daf34 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index f2be01e7bb6e6..a028213130b8f 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index ed242946d7fed..021bcdbd3a39a 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 376859461862b..718dec065a26e 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index 1d527daf9fe7b..d6b640edf0ad7 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index f46079d6881e3..fcbd593432e5f 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index b3b2827009dba..9f97e1833832a 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index 787add3df955f..dcf87716624a1 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index bc769ab13d5fc..f6c1f56249e94 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 271458277efa8..79042522e66ac 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index c5abfa2f6d863..15e899799f590 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index ec5ec473e51f5..18e9ef6ccac45 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 220a067aef038..326e27dbc1edb 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 42aa6396bddf2..c24741ae5838e 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index b90428765c016..d9fc4da298784 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 93013ece93211..b62ff38b32357 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 0551505eccd7e..dad70e27acc43 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index 33c14a78fffa6..acb8f05fc9a9c 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 83894d1b8a33f..1da467d78946a 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 39c2ee8740c63..99ce515f12797 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 3f190d880c181..78b159836aef3 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 162587bf80440..312fccd054c47 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index 43ce6a7a89d44..ce3125fc94091 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index f7caa43f7b1b2..f98ad3c88c6f5 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index 0250434a5220e..c358c03853b88 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index 935813883ef49..fcaf221b29043 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index c0f6b5ce45d1b..449bd40dd901d 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 34eb14385efdf..983800f85e786 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 4e5ee154f510a..b4e6f07b13746 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 5f690fa5c43f5..c76a5552a7e6e 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index d8b6f33e26e09..c666fdb7efac4 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index fb8de339bcb5f..09c664ec88bd7 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index d25b86b434745..3ef7af4c5abf5 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index 087abc9d3477e..5ffc0c80f6e2f 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index a832946fb50db..02df932ebc096 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 2f56e88a60610..69a6525d74894 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 7c1c4c3d7cc4e..6ea53d1c9b045 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 0df04baeebac9..a7d3792b8f486 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index a057e6d8f3eea..5fa4a4fcf7086 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index d088dfde31988..6f9cd35248b6f 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index cead63de20888..94839211fe301 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index b45b143509af5..9ff57dc422a9c 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 42fd1949c0e26..98159f4cf2280 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index 2505328c43588..f740bd50809bc 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index ce10d06efc1fc..001480a720e6f 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 609e77459f219..8488046346508 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 7d58080ca2a12..756afd28a2ab0 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index da1a3778f7783..11b17c59d9407 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index 0bde06967dda6..e69066fd658e1 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index efe59adaaf705..37abcd2c84e2d 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index 360a835c0b813..bdd641263c617 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index b3c93d877cb52..90a65998ca67f 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index f949341ad23fc..038ff15309377 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 8dfbed0c2e374..6b2f9f66f52b9 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index e805d1ccfbe44..b41876872e32d 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 191dd9b251b31..e78f88e0c0390 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 98035d41c5afb..58980921c9853 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index d617301a58b37..2bedfa2a2a67d 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index b40a969effdd4..84908b9d83e93 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 9c8c7943ce4c9..57add91686671 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 8b11a0d005670..75e8b6e02cab9 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index a6a201e51575d..0a308044f408d 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index f8e99283c1dc1..a76b273d3ee3a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 79a6765d56864..37c0af20730c7 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 2dbebdd9ab634..2cdba9ff6e844 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index aa5b4ccb778da..0a44a59c2184b 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 36b633bf8080b..cc798934af389 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index c6d84494e6729..c6fddc888402e 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 57e577dc535d4..4123577f698e9 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 302d9ed27d69e..19b620356d341 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index ded4ac6ec786d..ee79198737c8d 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 85f7d1e8d33b5..7669491b384b8 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index a870c501d0b99..91f4cc1e346d9 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index 9cb5f05c9c9d4..bf6f18a661ff3 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index bed979963c456..ec4b06b70ea5f 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 1d5b8f048c51c..9cc367fd800b1 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index de139b756e65e..4ffe510c673b5 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 866f0472e4607..a93d15bb0b582 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 3d5b513953f04..dfe594f59dc58 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index 5c431930553e9..6f8554f49d68c 100644 --- a/api_docs/kbn_solution_nav_oblt.mdx +++ b/api_docs/kbn_solution_nav_oblt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-oblt title: "@kbn/solution-nav-oblt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-oblt plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-oblt'] --- import kbnSolutionNavObltObj from './kbn_solution_nav_oblt.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 836de80fbfed1..68a5655feb3ac 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index 338d932a75536..d6b473077c262 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 0f7ab59b8021e..d4efc8b1a3ff2 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 39aec9016c710..4fbb610874f04 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index a9ac9b840a32a..fc2864d5177c8 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 9a5534d65f9eb..36069cc7e5da7 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index e09190f34ff45..c174c58d92058 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 4349eb5baf3cc..228abae8fe492 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 887505e269a3e..b25c3c2800b5d 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 52221555fb1e3..28ea933713110 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index daf123b33595e..4eea013748afa 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index e427cb805626c..d7456b738b638 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 8339cad27f4e6..9600f3f8ecf6f 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index 01cf31aa5c1e1..a5c0aedac2b89 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index 5b1f63ef8e683..edf52c8cc4395 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index fcf2a97f8004f..0bb9b907b443b 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index a6b842b84cd87..69abd42f74ad8 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index ca61db3b994a4..3c2a972fb78eb 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 1ee06fbefe3b4..cc8be5888164f 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 1a2cc34462d6e..6f4c9d037d610 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index df69eb97a4610..a2df8fe1129db 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index e88913138a121..b736edb28cc59 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index f2ad354d131ef..437f2fc9b4b32 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index fa1a01746b82e..fd5e6e76735e6 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index 4f415ea754ca6..eda1dd3807b63 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index a844ade5f8150..5d213f7e38db8 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index f44808dfd94b5..a83a7ba0716bf 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 2846f9ae955bf..c217cad220205 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 5a105bd1b1628..bda43243d4ad5 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index dc7c380351583..f8550e8934138 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index a24dc36df0b74..e18522f867474 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 95adab044f93f..8619fa38b538b 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 9a69a537f997f..76d5784f088a5 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index 7fdf2edf0b724..b9427f82c7946 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 80f9528c79b9c..69fd5beb8b033 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 13940fc995bb4..9232b879706ff 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 890cfbde9988a..d1ec3a99606d2 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 6487e77309ea5..bb3a7b4c023cc 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index 7efbc4421f997..ef3ff0add2502 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 0e525faa40316..8854d670d581e 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index f0a62c68037d3..e09bc8cc4cb27 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 99b5540b95365..9ea1c3dcc9648 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 12f5822d23343..6798f9529633f 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 0c855e819ff00..8c92df255302b 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 728d9b057af81..254cb5879f686 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index 5659347920a3b..ced198a674de2 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 181401aeb3496..3f3cf17ff3716 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b0e1c9868b24e..5f97bdfb6aa88 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 61596cfd5c529..104f76c0aaec8 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 36bf2cce24b12..3a627a8747839 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 00b4bbeb12335..5210a1fd9593b 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 2e0332bc6dd40..13cfed8c32f8f 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index c6b6c104c99e3..203c7d6952941 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index c5c2764b8c04b..5a0c1e2017abf 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 77e978621e5ae..cf3d8179ff771 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index aac1480b13c94..bf67f754011df 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index a93cf40853939..4e90a5b253dd2 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 16be9735cd09c..4fd67b0e06c65 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index a530caba89476..005246404174f 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 29edb72641aca..8a7fa5a2caadf 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index eafefdae2bd06..ff1ea8744ed83 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index 6b302b9fdaaa7..dc537b2f2d403 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 11c98224c454d..22d904218d2cb 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index 74c27896ce0cf..ba44f8d10d3ad 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index a792a6380b547..69bcdbdb4dea7 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index 39f19d98f12ae..d7415511d62ad 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index d091ccf300fa3..30104916fc40f 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index bf4d00c4d8667..56b998c077d73 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index c7b4c8909f99d..c128d6c77dee6 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 48444 | 241 | 36942 | 1864 | +| 48446 | 241 | 36944 | 1864 | ## Plugin Directory @@ -98,7 +98,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 84 | 0 | 84 | 8 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 0 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1309 | 5 | 1188 | 66 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1310 | 5 | 1189 | 66 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 72 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -493,7 +493,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 63 | 1 | 63 | 6 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 52 | 0 | 50 | 0 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 193 | 0 | 183 | 10 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 194 | 0 | 184 | 10 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 52 | 0 | 52 | 1 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 38 | 0 | 14 | 1 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index 0f2edcde88fd0..f2e2691c3a11a 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index c220246f99db1..188aeee26d864 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 359322b10b9c7..87a97a4fff1c8 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 427a3486dfa37..0262ba7413138 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 5ac0c888eef18..728df6c4da6de 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 5de7bd244fb16..7b11bd86c3c73 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index 4b5ac71d070b3..d59cd6548a15b 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 284720f78a1f1..4c20d7b9fab56 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 47a059050556c..cc4cc5ab4e695 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 3be4ec99b5658..c112f80f4a56c 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 3f5a6e0986623..d84ad5b34666e 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 8a283fd58b907..720c89c9c1a07 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 10f1c54e86a4e..d151f3195927b 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 095f74d720395..92e475aa39f67 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index fe6d6a93f56eb..7bee8300eb540 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 54aec683bc94e..38c6a8d0fcc59 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index e4ac4bd9ab089..231bcb2d2d533 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index 000c11d8dbbc7..f1c683fb0748d 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index 3e26e0fe9c2de..75c98b3be8bf2 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 60b5d8f545af8..05c693117a837 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index dc3c19666d11f..30453dd9c694d 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 5986bb936f2dc..bda3227864531 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index c3851a35558f4..298bd41f73b5c 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index e32f42d5cf492..08ff77a4cfae3 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 28946405d1c4a..102ab7c7105c5 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index 7579dba45f9d9..f026346acd676 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index dbfcdf77d6cee..bcf48ef39606a 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 5987b54820048..710bd770f896b 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 72c565c6f7e87..ba59a583a7618 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 852ec8fda1a71..3ae4618eca759 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 47417e4799866..095a36a6ee964 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index d02bce54d307d..6824f54bea5e4 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index 66b393185c3bb..b21d67d110f2f 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 1e60ae211a315..af57ec3c9d24b 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 6d835b73a9fab..f276bd6db21d5 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 22866b6c8c728..b2d0b5fab7ced 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 080cd42d8e793..6019b2e5d002c 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index f4e0ab3f3d72c..7695b653d7824 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 2f0f5ea28b22d..5866d778b46d1 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index caf66bd513b2a..b2d363a49f1da 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index f608b7b700ff1..6fd89ca19a002 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 755868cb98640..601b3658ded6a 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index b1990aef57cdb..e6a7ff5508aae 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index eaa5e23d3c291..9aaf4e432015a 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 64a5cb9d40ceb..971f94d248e12 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 31877ae7e09db..e9741083768d5 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index d315349b0d491..609595107d987 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 0152f601b7c30..84e6a1819d111 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 3941dac118646..f9cdb5e94c6c6 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index c3bbc3ce2e333..bd525a0e518d5 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 00788dee33478..ff645649148c4 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 956b46ce6a3b1..1a27ce90a9511 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 362186a7c033b..5ed4c1db33ed2 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index eb7d4b2f31734..ac14c74d0b776 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index 4b1379baf3e52..e035d5c82535f 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index f47bb32dad01d..ad506284484e7 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index a38b4ed0a5d16..f4a58ea922220 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index 336b5362b2209..ac7b57fe7cf35 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 301929b876ce6..55df8f57187bf 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index f5bfe4363f07f..3713b397cc268 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index fe35fa07fcaaf..b98e6a52b9387 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index f944581881feb..04b1ca8a4bc46 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 1148f54c22285..9a42b9ac4d2b8 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index cb43d04403c35..919231244721f 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 873000edb0550..ae662428e9820 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-05-24 +date: 2024-05-27 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From a4f468b97b7981170825c37371676be2696c7f68 Mon Sep 17 00:00:00 2001 From: Jedr Blaszyk Date: Mon, 27 May 2024 10:51:56 +0200 Subject: [PATCH 03/59] [Connectors] Use Connector API to cancel all syncs (#183924) --- .../lib/cancel_syncs.test.ts | 93 +++++++------------ .../kbn-search-connectors/lib/cancel_syncs.ts | 82 ++++------------ .../lib/fetch_sync_jobs.test.ts | 26 ++++++ .../lib/fetch_sync_jobs.ts | 7 +- packages/kbn-search-connectors/tsconfig.json | 1 + 5 files changed, 83 insertions(+), 126 deletions(-) diff --git a/packages/kbn-search-connectors/lib/cancel_syncs.test.ts b/packages/kbn-search-connectors/lib/cancel_syncs.test.ts index 05c2f5fcd529d..0fd769099a155 100644 --- a/packages/kbn-search-connectors/lib/cancel_syncs.test.ts +++ b/packages/kbn-search-connectors/lib/cancel_syncs.test.ts @@ -8,84 +8,57 @@ import { ElasticsearchClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '..'; import { SyncStatus } from '../types/connectors'; import { cancelSyncs } from './cancel_syncs'; +import { fetchSyncJobs } from './fetch_sync_jobs'; + +jest.mock('./fetch_sync_jobs', () => ({ + fetchSyncJobs: jest.fn(), +})); describe('cancelSync lib function', () => { const mockClient = { - update: jest.fn(), - updateByQuery: jest.fn(), + transport: { + request: jest.fn(), + }, }; beforeEach(() => { jest.clearAllMocks(); - const now = new Date('2022-05-22T10:10:11.111Z'); - jest.spyOn(Date, 'now').mockImplementation(() => now.getTime()); }); - it('should call updateByQuery to cancel syncs', async () => { - mockClient.updateByQuery.mockImplementation(() => ({ _id: 'fakeId' })); + it('should call /_cancel endpoint to cancel syncs', async () => { + (fetchSyncJobs as jest.Mock) + .mockResolvedValueOnce({ + data: [{ id: 'job_1' }, { id: 'job_2' }], + }) + .mockResolvedValueOnce({ data: [] }) + .mockResolvedValueOnce({ data: [{ id: 'job_3' }] }); await expect( cancelSyncs(mockClient as unknown as ElasticsearchClient, 'connectorId') ).resolves.toEqual(undefined); - expect(mockClient.updateByQuery).toHaveBeenCalledTimes(2); - expect(mockClient.updateByQuery).toHaveBeenCalledWith({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': 'connectorId', - }, - }, - { - terms: { - status: [SyncStatus.PENDING, SyncStatus.SUSPENDED], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELED}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['canceled_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['completed_at'] = '${new Date(Date.now()).toISOString()}';`, - }, + expect(fetchSyncJobs).toHaveBeenCalledTimes(3); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_1/_cancel', }); - expect(mockClient.updateByQuery).toHaveBeenCalledWith({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': 'connectorId', - }, - }, - { - terms: { - status: [SyncStatus.IN_PROGRESS], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELING}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}';`, - }, + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_2/_cancel', + }); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/_sync_job/job_3/_cancel', }); - await expect(mockClient.update).toHaveBeenCalledWith({ - doc: { last_sync_status: SyncStatus.CANCELED, sync_now: false }, - id: 'connectorId', - index: CONNECTORS_INDEX, + await expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'PUT', + path: '/_connector/connectorId/_last_sync', + body: { + last_access_control_sync_status: SyncStatus.CANCELED, + last_sync_status: SyncStatus.CANCELED, + }, }); }); }); diff --git a/packages/kbn-search-connectors/lib/cancel_syncs.ts b/packages/kbn-search-connectors/lib/cancel_syncs.ts index c706dd7d311e3..df66ceee5864e 100644 --- a/packages/kbn-search-connectors/lib/cancel_syncs.ts +++ b/packages/kbn-search-connectors/lib/cancel_syncs.ts @@ -7,75 +7,31 @@ */ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import { asyncForEach } from '@kbn/std'; -import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX } from '..'; +import { fetchSyncJobs, cancelSync } from '..'; import { SyncStatus } from '../types/connectors'; -import { isIndexNotFoundException } from '../utils/identify_exceptions'; export const cancelSyncs = async ( client: ElasticsearchClient, connectorId: string ): Promise => { - try { - await client.updateByQuery({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': connectorId, - }, - }, - { - terms: { - status: [SyncStatus.PENDING, SyncStatus.SUSPENDED], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELED}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['canceled_at'] = '${new Date(Date.now()).toISOString()}'; -ctx._source['completed_at'] = '${new Date(Date.now()).toISOString()}';`, - }, - }); - await client.updateByQuery({ - index: CONNECTORS_JOBS_INDEX, - query: { - bool: { - must: [ - { - term: { - 'connector.id': connectorId, - }, - }, - { - terms: { - status: [SyncStatus.IN_PROGRESS], - }, - }, - ], - }, - }, - script: { - lang: 'painless', - source: `ctx._source['status'] = '${SyncStatus.CANCELING}'; -ctx._source['cancelation_requested_at'] = '${new Date(Date.now()).toISOString()}';`, - }, - }); - await client.update({ - doc: { last_sync_status: SyncStatus.CANCELED, sync_now: false }, - id: connectorId, - index: CONNECTORS_INDEX, - }); - } catch (error) { - if (isIndexNotFoundException(error)) { - return; + await asyncForEach( + [SyncStatus.PENDING, SyncStatus.IN_PROGRESS, SyncStatus.SUSPENDED], + async (status) => { + const syncJobsToCancel = await fetchSyncJobs(client, connectorId, 0, 1000, 'all', status); + await asyncForEach(syncJobsToCancel.data, async (syncJob) => { + await cancelSync(client, syncJob.id); + }); } - throw error; - } + ); + + return await client.transport.request({ + method: 'PUT', + path: `/_connector/${connectorId}/_last_sync`, + body: { + last_access_control_sync_status: SyncStatus.CANCELED, + last_sync_status: SyncStatus.CANCELED, + }, + }); }; diff --git a/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts b/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts index c22b2ab19eef9..248c163f5dadb 100644 --- a/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts +++ b/packages/kbn-search-connectors/lib/fetch_sync_jobs.test.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { SyncStatus } from '../types'; import { fetchSyncJobs } from './fetch_sync_jobs'; describe('fetchSyncJobs lib', () => { @@ -41,5 +42,30 @@ describe('fetchSyncJobs lib', () => { querystring: 'from=0&size=10&connector_id=id&job_type=full,incremental', }); }); + + it('should fetch sync jobs by status', async () => { + mockClient.transport.request.mockImplementationOnce(() => ({ + count: 22, + results: [], + })); + await expect( + fetchSyncJobs(mockClient as any, 'id', 0, 10, 'content', SyncStatus.IN_PROGRESS) + ).resolves.toEqual({ + _meta: { + page: { + from: 0, + has_more_hits_than_total: true, + size: 10, + total: 22, + }, + }, + data: [], + }); + expect(mockClient.transport.request).toHaveBeenCalledWith({ + method: 'GET', + path: '/_connector/_sync_job', + querystring: 'from=0&size=10&connector_id=id&job_type=full,incremental&status=in_progress', + }); + }); }); }); diff --git a/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts b/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts index d278c97c84f6b..fec6be0cc7eb2 100644 --- a/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts +++ b/packages/kbn-search-connectors/lib/fetch_sync_jobs.ts @@ -9,7 +9,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { ConnectorsAPISyncJobResponse } from '..'; -import { ConnectorSyncJob } from '../types/connectors'; +import { ConnectorSyncJob, SyncStatus } from '../types/connectors'; import { Paginate } from '../types/pagination'; export const fetchSyncJobs = async ( @@ -17,13 +17,14 @@ export const fetchSyncJobs = async ( connectorId?: string, from: number = 0, size: number = 100, - syncJobType: 'content' | 'access_control' | 'all' = 'all' + syncJobType: 'content' | 'access_control' | 'all' = 'all', + syncStatus?: SyncStatus ): Promise> => { const querystring = `from=${from}&size=${size}${ connectorId ? '&connector_id=' + connectorId : '' }${syncJobType === 'content' ? '&job_type=full,incremental' : ''}${ syncJobType === 'access_control' ? '&job_type=access_control' : '' - }`; + }${syncStatus ? '&status=' + syncStatus : ''}`; const result = await client.transport.request({ method: 'GET', path: `/_connector/_sync_job`, diff --git a/packages/kbn-search-connectors/tsconfig.json b/packages/kbn-search-connectors/tsconfig.json index eb7decb3d1e00..cb54e57748e94 100644 --- a/packages/kbn-search-connectors/tsconfig.json +++ b/packages/kbn-search-connectors/tsconfig.json @@ -24,5 +24,6 @@ "@kbn/config-schema", "@kbn/i18n-react", "@kbn/test-jest-helpers", + "@kbn/std", ] } From cbe2abfd9f0878bada8523718ef1804ec7924f50 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 27 May 2024 11:44:58 +0200 Subject: [PATCH 04/59] [Infra] Fix Docker disk charts: Change disk with diskio (#184042) Closes #184038 ## Summary This PR fixes Docker disk charts by changing `disk` with `diskio`. ## Testing - Collect docker container metrics (for example using metricbeat) - Note: The new Container view flag in Infra settings should be disabled - Open Inventory and switch the `Show` setting to Docker Containers - Open the details page using the metrics link - The disk charts should be visible: ![image](https://github.com/elastic/kibana/assets/14139027/5b8690c9-e4aa-4fb3-893f-28c77064ba15) --- .../container/metrics/tsvb/container_disk_io_bytes.ts | 2 +- .../container/metrics/tsvb/container_diskio_ops.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts index 3ba2b11740b6e..7320349f92e8d 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_disk_io_bytes.ts @@ -13,7 +13,7 @@ export const containerDiskIOBytes: TSVBMetricModelCreator = ( interval ): TSVBMetricModel => ({ id: 'containerDiskIOBytes', - requires: ['docker.disk'], + requires: ['docker.diskio'], index_pattern: indexPattern, interval, time_field: timeField, diff --git a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts index 772c0d6c0a33e..a6887fb4e7638 100644 --- a/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts +++ b/x-pack/plugins/observability_solution/metrics_data_access/common/inventory_models/container/metrics/tsvb/container_diskio_ops.ts @@ -13,7 +13,7 @@ export const containerDiskIOOps: TSVBMetricModelCreator = ( interval ): TSVBMetricModel => ({ id: 'containerDiskIOOps', - requires: ['docker.disk'], + requires: ['docker.diskio'], index_pattern: indexPattern, interval, time_field: timeField, From 982dfa3551b430fb1749024847d8e39ec2333894 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Mon, 27 May 2024 07:13:56 -0300 Subject: [PATCH 05/59] [Infra] Deprecate fields returned by the `/api/metrics/source` endpoint, in favor of ad hoc data views (#181954) closes [#180689](https://github.com/elastic/kibana/issues/180689) ## Summary This PR lays the necessary foundation to deprecate the indices [fields list attribute retrieved and returned](https://github.com/elastic/kibana/blob/main/x-pack/plugins/observability_solution/infra/server/routes/metrics_sources/index.ts#L38) by the `api/metrics/source` endpoint and affects all infra pages. Now instead of the Metrics UI and Inventory UI relying on that attribute to build the list of suggestions in their search bars, an adhoc data view will be created from the `metricsAlias` returned by the same endpoint. The data view object contains everything that's needed with the benefit of allowing us to make async requests and cache, provided by Data Views Service. This will allow us to start moving away from `api/metrics/source` to retrieve information critical to the loading of the pages. It's a big PR, and the changes affect mostly the search bars, field name selectors, and charts across infra. Besides, it also contains - Storybook fix. - Some components were turned into functional components - `` now encapsulates error messages and is only used in Infrastructure pages - Improvement of the Infrastructure routing code. - Data View provider common to all infra pages - `SourceProvider` refactor. image image image image image image image ## How to test - Start a local kibana instance pointing to an Oblt cluster - Navigate to Inventory, and all its pages, check if charts, search bars and field name autocomplete fields are working --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../infra/common/constants.ts | 5 + .../inventory/components/expression.test.tsx | 47 ++- .../inventory/components/expression.tsx | 26 +- .../alerting/inventory/components/metric.tsx | 9 +- .../alert_details_app_section.test.tsx.snap | 7 - .../alert_details_app_section.test.tsx | 8 - .../components/alert_details_app_section.tsx | 11 +- .../custom_equation_editor.stories.tsx | 19 - .../custom_equation_editor.tsx | 9 +- .../custom_equation/metric_row_with_count.tsx | 4 - .../components/expression.test.tsx | 29 +- .../components/expression.tsx | 40 +- .../components/expression_chart.test.tsx | 51 +-- .../components/expression_chart.tsx | 14 +- .../components/expression_row.test.tsx | 56 ++- .../components/expression_row.tsx | 33 +- .../hooks/use_metrics_explorer_chart_data.ts | 26 +- .../infra/public/apps/metrics_app.tsx | 28 +- .../context/fixtures/asset_details_props.ts | 1 - .../asset_details/__stories__/decorator.tsx | 37 +- .../asset_details/asset_details.tsx | 10 +- .../components/asset_details/charts/chart.tsx | 22 +- .../asset_details/context_providers.tsx | 2 +- .../asset_details/hooks/use_data_views.ts | 11 +- .../asset_details/hooks/use_process_list.ts | 12 +- .../public/components/asset_details/types.ts | 3 - .../anomaly_detection_flyout.tsx | 14 +- .../ml/anomaly_detection/job_setup_screen.tsx | 16 +- .../public/containers/metrics_source/index.ts | 2 +- .../containers/metrics_source/metrics_view.ts | 54 +++ .../containers/metrics_source/source.tsx | 90 ++--- .../public/containers/metrics_source/types.ts | 15 - .../containers/with_kuery_autocompletion.tsx | 24 +- .../infra/public/hooks/use_data_view.test.ts | 79 ---- .../infra/public/hooks/use_data_view.ts | 49 --- .../infra/public/hooks/use_inventory_views.ts | 4 +- .../hooks/use_metrics_explorer_views.ts | 4 +- .../infra/public/pages/404.tsx | 14 +- .../redirect_to_host_detail_via_ip.tsx | 9 +- .../host_details_flyout/flyout_wrapper.tsx | 7 +- .../hosts/components/hosts_container.tsx | 35 +- .../hosts/components/kpis/kpi_charts.tsx | 8 +- .../search_bar/unified_search_bar.tsx | 8 +- .../hosts/components/tabs/metrics/chart.tsx | 28 +- .../components/tabs/metrics/metrics_grid.tsx | 7 +- .../metrics/hosts/hooks/use_host_count.ts | 17 +- .../hosts/hooks/use_hosts_table.test.ts | 16 +- .../metrics/hosts/hooks/use_hosts_table.tsx | 11 +- .../hosts/hooks/use_metrics_data_view.ts | 26 -- .../metrics/hosts/hooks/use_unified_search.ts | 8 +- .../public/pages/metrics/hosts/index.tsx | 30 +- .../infra/public/pages/metrics/index.tsx | 50 +-- .../inventory_view/components/search_bar.tsx | 8 +- .../metrics_and_groupby_toolbar_items.tsx | 2 - .../components/toolbars/toolbar_wrapper.tsx | 3 - .../components/toolbars/types.ts | 2 - .../waffle/asset_details_flyout.tsx | 8 +- .../components/waffle/custom_field_panel.tsx | 125 +++--- .../metric_control/custom_metric_form.tsx | 363 +++++++++--------- .../waffle/metric_control/index.tsx | 6 +- .../waffle/waffle_group_by_controls.tsx | 291 +++++++------- .../hooks/use_waffle_filters.test.ts | 24 +- .../hooks/use_waffle_filters.ts | 9 +- .../pages/metrics/inventory_view/index.tsx | 24 -- .../metric_detail/asset_detail_page.tsx | 24 -- .../components/node_details_page.tsx | 3 - .../metric_detail/metric_detail_page.tsx | 4 +- .../metrics_explorer/components/chart.tsx | 5 - .../components/chart_context_menu.test.tsx | 52 ++- .../components/chart_context_menu.tsx | 22 +- .../metrics_explorer/components/charts.tsx | 7 +- .../metrics_explorer/components/group_by.tsx | 15 +- .../helpers/create_tsvb_link.test.ts | 53 +-- .../components/helpers/create_tsvb_link.ts | 9 +- .../metrics_explorer/components/kuery_bar.tsx | 12 +- .../metrics_explorer/components/metrics.tsx | 12 +- .../metrics_explorer/components/toolbar.tsx | 16 +- .../hooks/use_metric_explorer_state.test.tsx | 10 +- .../hooks/use_metric_explorer_state.ts | 16 +- .../hooks/use_metrics_explorer_data.test.tsx | 36 +- .../hooks/use_metrics_explorer_data.ts | 34 +- .../pages/metrics/metrics_explorer/index.tsx | 143 ++++--- .../public/pages/metrics/page_template.tsx | 51 ++- .../source_configuration_settings.tsx | 18 +- .../infra/public/utils/data_view.ts | 85 ++++ .../infra/public/utils/filters/build.ts | 2 +- .../infra/public/utils/kuery.ts | 2 +- 87 files changed, 1231 insertions(+), 1410 deletions(-) create mode 100644 x-pack/plugins/observability_solution/infra/public/containers/metrics_source/metrics_view.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/containers/metrics_source/types.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts delete mode 100644 x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts create mode 100644 x-pack/plugins/observability_solution/infra/public/utils/data_view.ts diff --git a/x-pack/plugins/observability_solution/infra/common/constants.ts b/x-pack/plugins/observability_solution/infra/common/constants.ts index e4621f63793c2..d0f48068dda64 100644 --- a/x-pack/plugins/observability_solution/infra/common/constants.ts +++ b/x-pack/plugins/observability_solution/infra/common/constants.ts @@ -37,3 +37,8 @@ export const O11Y_AAD_FIELDS = [ export const LINK_TO_INVENTORY = '/app/metrics/link-to/inventory'; export const METRICS_EXPLORER_URL = '/app/metrics/explorer'; export const fifteenMinutesInMilliseconds = 15 * 60 * 1000; + +export const DEFAULT_METRICS_VIEW_ATTRIBUTES = { + name: 'Metrics View', + timeFieldName: TIMESTAMP_FIELD, +}; diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.test.tsx index c39fbc8e0f0c0..8eae693451e63 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.test.tsx @@ -8,18 +8,47 @@ import { mountWithIntl, nextTick, shallowWithIntl } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; +import { DataView, type FieldSpec } from '@kbn/data-views-plugin/common'; // We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` import { coreMock as mockCoreMock } from '@kbn/core/public/mocks'; import { Comparator, InventoryMetricConditions } from '../../../../common/alerting/metrics'; -import { SnapshotCustomMetricInput } from '../../../../common/http_api/snapshot_api'; import { AlertContextMeta, defaultExpression, ExpressionRow, Expressions } from './expression'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; - -jest.mock('../../../containers/metrics_source/source', () => ({ +import { ResolvedDataView } from '../../../utils/data_view'; +import { TIMESTAMP_FIELD } from '../../../../common/constants'; +import type { SnapshotCustomMetricInput } from '../../../../common/http_api'; + +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + fields: [ + { + name: 'some.system.field', + type: 'bzzz', + searchable: true, + aggregatable: true, + }, + ] as Partial, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -28,7 +57,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ services: mockCoreMock.createStart(), }), })); - const exampleCustomMetric = { id: 'this-is-an-id', field: 'some.system.field', @@ -174,15 +202,6 @@ describe('ExpressionRow', () => { metric: [], }} expression={expression} - fields={[ - { - name: 'some.system.field', - type: 'bzzz', - searchable: true, - aggregatable: true, - displayable: true, - }, - ]} /> ); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx index 56bc91f3613f5..ce88a2112e528 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx @@ -64,7 +64,7 @@ import { } from '../../../../common/http_api/snapshot_api'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { - DerivedIndexPattern, + useMetricsDataViewContext, useSourceContext, withSourceProvider, } from '../../../containers/metrics_source'; @@ -120,15 +120,12 @@ export const defaultExpression = { export const Expressions: React.FC = (props) => { const { setRuleParams, ruleParams, errors, metadata } = props; - const { source, createDerivedIndexPattern } = useSourceContext(); + const { source } = useSourceContext(); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); + const { metricsView } = useMetricsDataViewContext(); const updateParams = useCallback( (id, e: InventoryMetricConditions) => { @@ -166,13 +163,13 @@ export const Expressions: React.FC = (props) => { try { setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference, false) || '' ); } catch (e) { setRuleParams('filterQuery', QUERY_INVALID); } }, - [derivedIndexPattern, setRuleParams] + [metricsView?.dataViewReference, setRuleParams] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -247,10 +244,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', md.filter); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(md.filter, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery(md.filter, metricsView?.dataViewReference) || '' ); } - }, [metadata, derivedIndexPattern, setRuleParams]); + }, [metadata, metricsView?.dataViewReference, setRuleParams]); useEffect(() => { const md = metadata; @@ -276,7 +273,7 @@ export const Expressions: React.FC = (props) => { if (!ruleParams.sourceId) { setRuleParams('sourceId', source?.id || 'default'); } - }, [metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps + }, [metadata, metricsView?.dataViewReference, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps return ( <> @@ -314,7 +311,6 @@ export const Expressions: React.FC = (props) => { setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} - fields={derivedIndexPattern.fields} > = (props) => { > {metadata ? ( ): void; - fields: DerivedIndexPattern['fields']; } const NonCollapsibleExpressionCss = css` @@ -450,8 +444,7 @@ const StyledHealthCss = css` export const ExpressionRow: FC> = (props) => { const [isExpanded, toggle] = useToggle(true); - const { children, setRuleParams, expression, errors, expressionId, remove, canDelete, fields } = - props; + const { children, setRuleParams, expression, errors, expressionId, remove, canDelete } = props; const { metric, comparator = Comparator.GT, @@ -619,7 +612,6 @@ export const ExpressionRow: FC> = (props) onChangeCustom={updateCustomMetric} errors={errors} customMetric={customMetric} - fields={fields} /> {!displayWarningThreshold && criticalThresholdExpression} diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/metric.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/metric.tsx index d6704ce6c2b89..dfe22251fb599 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/metric.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/metric.tsx @@ -24,6 +24,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { debounce } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { getCustomMetricLabel } from '../../../../common/formatters/get_custom_metric_label'; import { SnapshotCustomAggregation, @@ -32,7 +33,6 @@ import { SnapshotCustomMetricInputRT, SNAPSHOT_CUSTOM_AGGREGATIONS, } from '../../../../common/http_api/snapshot_api'; -import { DerivedIndexPattern } from '../../../containers/metrics_source'; interface Props { metric?: { value: string; text: string }; @@ -41,7 +41,6 @@ interface Props { onChange: (metric?: string) => void; onChangeCustom: (customMetric?: SnapshotCustomMetricInput) => void; customMetric?: SnapshotCustomMetricInput; - fields: DerivedIndexPattern['fields']; popupPosition?: | 'upCenter' | 'upLeft' @@ -80,7 +79,6 @@ export const MetricExpression = ({ metric, metrics, customMetric, - fields, errors, onChange, onChangeCustom, @@ -90,6 +88,7 @@ export const MetricExpression = ({ const [customMetricTabOpen, setCustomMetricTabOpen] = useState(metric?.value === 'custom'); const [selectedOption, setSelectedOption] = useState(metric?.value); const [fieldDisplayedCustomLabel, setFieldDisplayedCustomLabel] = useState(customMetric?.label); + const { metricsView } = useMetricsDataViewContext(); // eslint-disable-next-line react-hooks/exhaustive-deps const firstFieldOption = { @@ -101,10 +100,10 @@ export const MetricExpression = ({ const fieldOptions = useMemo( () => - fields + (metricsView?.fields ?? []) .filter((f) => f.aggregatable && f.type === 'number' && !(customMetric?.field === f.name)) .map((f) => ({ label: f.name })), - [fields, customMetric?.field] + [metricsView?.fields, customMetric?.field] ); const expressionDisplayValue = useMemo(() => { diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap index 0d63415081910..72381f6e62d63 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/__snapshots__/alert_details_app_section.test.tsx.snap @@ -17,10 +17,6 @@ Array [ />, ], "chartType": "line", - "derivedIndexPattern": Object { - "fields": Array [], - "title": "metricbeat-*", - }, "expression": Object { "aggType": "count", "comparator": ">", @@ -38,9 +34,6 @@ Array [ "host-1", ], "hideTitle": true, - "source": Object { - "id": "default", - }, "timeRange": Object { "from": "2023-03-28T10:43:13.802Z", "to": "2023-03-29T13:14:09.581Z", diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx index a9d5b2c2193fc..f7827fcbcadbb 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.test.tsx @@ -48,14 +48,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); -jest.mock('../../../containers/metrics_source/source', () => ({ - withSourceProvider: () => jest.fn, - useSourceContext: () => ({ - source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), - }), -})); - describe('AlertDetailsAppSection', () => { const queryClient = new QueryClient(); const mockedSetAlertSummaryFields = jest.fn(); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx index 27c61aa8c18ca..feed66500a2a1 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect } from 'react'; import moment from 'moment'; import { EuiFlexGroup, @@ -33,7 +33,7 @@ import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-tim import { metricValueFormatter } from '../../../../common/alerting/metrics/metric_value_formatter'; import { TIME_LABELS } from '../../common/criterion_preview_chart/criterion_preview_chart'; import { Threshold } from '../../common/components/threshold'; -import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; +import { withSourceProvider } from '../../../containers/metrics_source'; import { generateUniqueKey } from '../lib/generate_unique_key'; import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; @@ -80,16 +80,11 @@ export function AlertDetailsAppSection({ setAlertSummaryFields, }: AppSectionProps) { const { uiSettings, charts } = useKibanaContextForPlugin().services; - const { source, createDerivedIndexPattern } = useSourceContext(); const { euiTheme } = useEuiTheme(); const groupInstance = alert.fields[ALERT_GROUP]?.map((group: Group) => group.value); const groups = alert.fields[ALERT_GROUP]; const tags = alert.fields[TAGS]; - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); const chartProps = { baseTheme: charts.theme.useChartsBaseTheme(), }; @@ -188,13 +183,11 @@ export function AlertDetailsAppSection({ diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx index d2fd0639859a6..ce30172a74f15 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.stories.tsx @@ -34,24 +34,6 @@ export default { }, } as Meta; -const fakeDataView = { - title: 'metricbeat-*', - fields: [ - { - name: 'system.cpu.user.pct', - type: 'number', - }, - { - name: 'system.cpu.system.pct', - type: 'number', - }, - { - name: 'system.cpu.cores', - type: 'number', - }, - ], -}; - const CustomEquationEditorTemplate: Story = (args) => { const [expression, setExpression] = useState(args.expression); const [errors, setErrors] = useState(args.errors); @@ -78,7 +60,6 @@ const CustomEquationEditorTemplate: Story = (args) => errors={errors} expression={expression} onChange={handleExpressionChange} - dataView={fakeDataView} /> ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx index 3b00563fcc0f4..c87ec88fa3ef9 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/custom_equation/custom_equation_editor.tsx @@ -13,6 +13,7 @@ import { EuiSpacer, } from '@elastic/eui'; import React, { useState, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { omit, range, first, xor, debounce } from 'lodash'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -40,7 +41,7 @@ export interface CustomEquationEditorProps { fields: NormalizedFields; aggregationTypes: AggregationTypes; errors: IErrorObject; - dataView: DataViewBase; + dataView?: DataViewBase; } const NEW_METRIC = { name: 'A', aggType: Aggregators.AVERAGE as CustomMetricAggTypes }; @@ -55,7 +56,6 @@ export const CustomEquationEditor = ({ fields, aggregationTypes, errors, - dataView, }: CustomEquationEditorProps) => { const [customMetrics, setCustomMetrics] = useState( expression?.customMetrics ?? [NEW_METRIC] @@ -133,7 +133,6 @@ export const CustomEquationEditor = ({ disableDelete={disableDelete} onChange={handleChange} errors={errors} - dataView={dataView} /> ); } @@ -183,7 +182,9 @@ export const CustomEquationEditor = ({ { const aggOptions = useMemo( () => @@ -95,7 +92,6 @@ export const MetricRowWithCount = ({ ({ +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -29,8 +49,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); -const dataViewMock = dataViewPluginMocks.createStartContract(); - describe('Expression', () => { async function setup(currentOptions: { metrics?: MetricsExplorerMetric[]; @@ -55,7 +73,6 @@ describe('Expression', () => { metadata={{ currentOptions, }} - dataViews={dataViewMock} /> ); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx index 814a968d9718e..542dd74eb1732 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx @@ -28,7 +28,11 @@ import { RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { useSourceContext, withSourceProvider } from '../../../containers/metrics_source'; +import { + useMetricsDataViewContext, + useSourceContext, + withSourceProvider, +} from '../../../containers/metrics_source'; import { Aggregators, Comparator, QUERY_INVALID } from '../../../../common/alerting/metrics'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; @@ -42,7 +46,13 @@ const FILTER_TYPING_DEBOUNCE_MS = 500; type Props = Omit< RuleTypeParamsExpressionProps, - 'defaultActionGroupId' | 'actionGroups' | 'charts' | 'data' | 'unifiedSearch' | 'onChangeMetaData' + | 'defaultActionGroupId' + | 'actionGroups' + | 'charts' + | 'data' + | 'unifiedSearch' + | 'onChangeMetaData' + | 'dataViews' >; const defaultExpression = { @@ -57,14 +67,11 @@ export { defaultExpression }; export const Expressions: React.FC = (props) => { const { setRuleParams, ruleParams, errors, metadata } = props; const { docLinks } = useKibanaContextForPlugin().services; - const { source, createDerivedIndexPattern } = useSourceContext(); + const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); const options = useMemo(() => { if (metadata?.currentOptions?.metrics) { @@ -113,13 +120,13 @@ export const Expressions: React.FC = (props) => { try { setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern, false) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference, false) || '' ); } catch (e) { setRuleParams('filterQuery', QUERY_INVALID); } }, - [setRuleParams, derivedIndexPattern] + [setRuleParams, metricsView?.dataViewReference] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -193,7 +200,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', md.currentOptions.filterQuery); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(md.currentOptions.filterQuery, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery( + md.currentOptions.filterQuery, + metricsView?.dataViewReference + ) || '' ); } else if (md && md.currentOptions?.groupBy && md.series) { const { groupBy } = md.currentOptions; @@ -203,10 +213,10 @@ export const Expressions: React.FC = (props) => { setRuleParams('filterQueryText', filter); setRuleParams( 'filterQuery', - convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' + convertKueryToElasticSearchQuery(filter, metricsView?.dataViewReference) || '' ); } - }, [metadata, derivedIndexPattern, setRuleParams]); + }, [metadata, metricsView?.dataViewReference, setRuleParams]); const preFillAlertGroupBy = useCallback(() => { const md = metadata; @@ -299,7 +309,6 @@ export const Expressions: React.FC = (props) => { return ( 1) || false} - fields={derivedIndexPattern.fields} remove={removeExpression} addExpression={addExpression} key={idx} // idx's don't usually make good key's but here the index has semantic meaning @@ -307,12 +316,9 @@ export const Expressions: React.FC = (props) => { setRuleParams={updateParams} errors={(errors[idx] as IErrorObject) || emptyError} expression={e || {}} - dataView={derivedIndexPattern} > @@ -395,7 +401,6 @@ export const Expressions: React.FC = (props) => { > {(metadata && ( = (props) => { > false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; const mockStartServices = mockCoreMock.createStart(); jest.mock('../../../hooks/use_kibana', () => ({ @@ -32,6 +42,23 @@ jest.mock('../../../hooks/use_kibana', () => ({ }), })); +jest.mock('../../../containers/metrics_source', () => ({ + withSourceProvider: () => jest.fn, + useSourceContext: () => ({ + source: { id: 'default' }, + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const mockResponse = { pageInfo: { afterKey: null, @@ -51,29 +78,9 @@ describe('ExpressionChart', () => { groupBy?: string, annotations?: Array> ) { - const derivedIndexPattern: DataViewBase = { - title: 'metricbeat-*', - fields: [], - }; - - const source: MetricsSourceConfiguration = { - id: 'default', - origin: 'fallback', - configuration: { - name: 'default', - description: 'The default configuration', - metricAlias: 'metricbeat-*', - inventoryDefaultView: 'host', - metricsExplorerDefaultView: 'host', - anomalyThreshold: 20, - }, - }; - const wrapper = mountWithIntl( >; chartType?: MetricsExplorerChartType; filterQuery?: string; groupBy?: string | string[]; groupInstance?: string | string[]; hideTitle?: boolean; - source?: MetricsSourceConfiguration; timeRange?: TimeRange; } export const ExpressionChart: React.FC = ({ expression, - derivedIndexPattern, annotations, chartType = MetricsExplorerChartType.bar, filterQuery, groupBy, groupInstance, hideTitle = false, - source, timeRange, }) => { const { charts } = useKibanaContextForPlugin().services; const chartTheme = useTimelineChartTheme(); - const { isLoading, data } = useMetricsExplorerChartData( + const { isLoading, data } = useMetricsExplorerChartData({ expression, - derivedIndexPattern, - source, filterQuery, groupBy, groupInstance, - timeRange - ); + timeRange, + }); const chartRef = useRef(null); const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.test.tsx index 4180b033a746a..964848b17a67f 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.test.tsx @@ -5,18 +5,53 @@ * 2.0. */ +import { ResolvedDataView } from '../../../utils/data_view'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; import { Comparator } from '../../../../common/alerting/metrics'; import { MetricExpression } from '../types'; import { ExpressionRow } from './expression_row'; +import { TIMESTAMP_FIELD } from '../../../../common/constants'; +import { DataView, type FieldSpec } from '@kbn/data-views-plugin/common'; -jest.mock('../../../containers/metrics_source/source', () => ({ +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + fields: [ + { + name: 'system.cpu.user.pct', + type: 'test', + searchable: true, + aggregatable: true, + }, + { + name: 'system.load.1', + type: 'test', + searchable: true, + aggregatable: true, + }, + ] as Partial, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../containers/metrics_source', () => ({ withSourceProvider: () => jest.fn, useSourceContext: () => ({ source: { id: 'default' }, - createDerivedIndexPattern: () => ({ fields: [], title: 'metricbeat-*' }), + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); @@ -25,22 +60,6 @@ describe('ExpressionRow', () => { const wrapper = mountWithIntl( {}} addExpression={() => {}} key={1} @@ -52,7 +71,6 @@ describe('ExpressionRow', () => { timeWindowSize: [], }} expression={expression} - dataView={{ fields: [], title: 'metricbeat-*' }} /> ); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx index 7804a9f15ab26..a3a5c2021307d 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -17,7 +17,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { omit } from 'lodash'; -import React, { useCallback, useMemo, useState, FC, PropsWithChildren } from 'react'; +import React, { PropsWithChildren, useCallback, useMemo, useState } from 'react'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { AggregationType, @@ -27,11 +27,10 @@ import { ThresholdExpression, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; -import { DataViewBase } from '@kbn/es-query'; import useToggle from 'react-use/lib/useToggle'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; import { decimalToPct, pctToDecimal } from '../../../../common/utils/corrected_percent_convert'; -import { DerivedIndexPattern } from '../../../containers/metrics_source'; import { AGGREGATION_TYPES, MetricExpression } from '../types'; import { CustomEquationEditor } from './custom_equation'; import { CUSTOM_EQUATION } from '../i18n_strings'; @@ -48,7 +47,6 @@ const customComparators = { }; interface ExpressionRowProps { - fields: DerivedIndexPattern['fields']; expressionId: number; expression: MetricExpression; errors: IErrorObject; @@ -56,7 +54,6 @@ interface ExpressionRowProps { addExpression(): void; remove(id: number): void; setRuleParams(id: number, params: MetricExpression): void; - dataView: DataViewBase; } const NegativeHorizontalMarginDiv = euiStyled.div`margin: 0 -4px;`; @@ -69,20 +66,17 @@ const StyledHealth = euiStyled(EuiHealth)` margin-left: 4px; `; -export const ExpressionRow: FC> = (props) => { +export const ExpressionRow = ({ + children, + setRuleParams, + expression, + errors, + expressionId, + remove, + canDelete, +}: PropsWithChildren) => { const [isExpanded, toggle] = useToggle(true); - - const { - dataView, - children, - setRuleParams, - expression, - errors, - expressionId, - remove, - fields, - canDelete, - } = props; + const { metricsView } = useMetricsDataViewContext(); const { aggType = AGGREGATION_TYPES.MAX, @@ -209,7 +203,7 @@ export const ExpressionRow: FC> = (props) /> ); - const normalizedFields = fields.map((f) => ({ + const normalizedFields = (metricsView?.fields ?? []).map((f) => ({ normalizedType: f.type, name: f.name, })); @@ -341,7 +335,6 @@ export const ExpressionRow: FC> = (props) aggregationTypes={aggregationType} onChange={handleCustomMetricChange} errors={errors} - dataView={dataView} /> diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts index 79e2e4aec33da..73bafecee1000 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts @@ -6,10 +6,8 @@ */ import DateMath from '@kbn/datemath'; -import { DataViewBase } from '@kbn/es-query'; import { useMemo } from 'react'; import { MetricExpressionCustomMetric } from '../../../../common/alerting/metrics'; -import { MetricsSourceConfiguration } from '../../../../common/metrics_sources'; import { MetricExpression, TimeRange } from '../types'; import { MetricsExplorerOptions, @@ -20,15 +18,19 @@ import { MetricExplorerCustomMetricAggregations } from '../../../../common/http_ const DEFAULT_TIME_RANGE = {}; -export const useMetricsExplorerChartData = ( - expression: MetricExpression, - derivedIndexPattern: DataViewBase, - source?: MetricsSourceConfiguration, - filterQuery?: string, - groupBy?: string | string[], - groupInstance?: string | string[], - timeRange: TimeRange = DEFAULT_TIME_RANGE -) => { +export const useMetricsExplorerChartData = ({ + expression, + filterQuery, + groupBy, + groupInstance, + timeRange = DEFAULT_TIME_RANGE, +}: { + expression: MetricExpression; + filterQuery?: string; + groupBy?: string | string[]; + groupInstance?: string | string[]; + timeRange?: TimeRange; +}) => { const { timeSize, timeUnit } = expression || { timeSize: 1, timeUnit: 'm' }; const options: MetricsExplorerOptions = useMemo( @@ -74,7 +76,7 @@ export const useMetricsExplorerChartData = ( }; }, [timeRange, timeSize, timeUnit]); - return useMetricsExplorerData(options, source?.configuration, derivedIndexPattern, timestamps); + return useMetricsExplorerData({ options, timestamps }); }; const mapMetricThresholdMetricToMetricsExplorerMetric = (metric: MetricExpressionCustomMetric) => { diff --git a/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx b/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx index 519befb57f8c0..ab6ffcb6b5299 100644 --- a/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx +++ b/x-pack/plugins/observability_solution/infra/public/apps/metrics_app.tsx @@ -18,7 +18,7 @@ import { InfrastructurePage } from '../pages/metrics'; import { InfraClientStartDeps, InfraClientStartExports } from '../types'; import { CommonInfraProviders, CoreProviders } from './common_providers'; import { prepareMountElement } from './common_styles'; -import { SourceProvider } from '../containers/metrics_source'; +import { SourceProvider, MetricsDataViewProvider } from '../containers/metrics_source'; import { PluginConfigProvider } from '../containers/plugin_config_context'; import type { KibanaEnvContext } from '../hooks/use_kibana'; @@ -97,18 +97,20 @@ const MetricsApp: React.FC<{ triggersActionsUI={plugins.triggersActionsUi} > - - - - - - {uiCapabilities?.infrastructure?.show && ( - - )} - - - - + + + + + + + {uiCapabilities?.infrastructure?.show && ( + + )} + + + + + diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts index 7e76b3fa1474b..ab3318c7cebc7 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/context/fixtures/asset_details_props.ts @@ -66,5 +66,4 @@ export const assetDetailsProps: AssetDetailsProps = { }, tabs, links, - metricAlias: 'metrics-*', }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx index 7a7f93a7c57f5..1c09b8dabc58a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/__stories__/decorator.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { JSXElementConstructor, ReactElement } from 'react'; +import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { KibanaContextProvider, @@ -19,15 +19,14 @@ import type { DeepPartial } from 'utility-types'; import type { LocatorPublic } from '@kbn/share-plugin/public'; import type { IKibanaSearchRequest, ISearchOptions } from '@kbn/search-types'; import type { SearchSessionState } from '@kbn/data-plugin/public'; -import { AlertSummaryWidget } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget/alert_summary_widget'; import type { Theme } from '@elastic/charts/dist/utils/themes/theme'; -import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget'; import { defaultLogViewAttributes } from '@kbn/logs-shared-plugin/common'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { MemoryRouter } from 'react-router-dom'; +import { AlertPrefillProvider } from '../../../alerting/use_alert_prefill'; import { PluginConfigProvider } from '../../../containers/plugin_config_context'; import type { PluginKibanaContextValue } from '../../../hooks/use_kibana'; -import { SourceProvider } from '../../../containers/metrics_source'; +import { MetricsDataViewProvider, SourceProvider } from '../../../containers/metrics_source'; import { getHttp } from './context/http'; import { assetDetailsProps, getLogEntries } from './context/fixtures'; import { ContextProviders } from '../context_providers'; @@ -36,6 +35,7 @@ import type { InfraConfig } from '../../../../server'; const settings: Record = { 'dateFormat:scaled': [['', 'HH:mm:ss.SSS']], + 'timepicker:timeDefaults': ['now-15m', 'now'], }; const getSettings = (key: string): any => settings[key]; @@ -57,6 +57,16 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { }, getUrlForApp: (url: string) => url, }, + chrome: { + docTitle: { + change(newTitle) { + action('chrome.docTitle.change')(newTitle); + return newTitle; + }, + }, + setBreadcrumbs: () => {}, + setBreadcrumbsAppendExtension: () => {}, + }, data: { search: { search: (request: IKibanaSearchRequest, options?: ISearchOptions) => { @@ -93,9 +103,8 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { get: () => ({ key: 'mock', defaultOverride: undefined } as any), }, triggersActionsUi: { - getAlertSummaryWidget: AlertSummaryWidget as ( - props: AlertSummaryWidgetProps - ) => ReactElement>, + getAlertSummaryWidget: () => <>, + getAlertsStateTable: () => <>, }, charts: { theme: { @@ -125,6 +134,9 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { navigate: async () => { return Promise.resolve(); }, + getRedirectUrl: (args: any) => { + action('share.url.locators.getRedirectUrl')(args); + }, } as unknown as LocatorPublic), }, }, @@ -154,6 +166,9 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { reportAssetDetailsPageViewed: () => {}, reportAssetDashboardLoaded: () => {}, }, + observabilityShared: { + navigation: { PageTemplate: ({ children }: { children?: any }) => <>{children} }, + }, }; const config: InfraConfig = { @@ -195,7 +210,11 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { - {story()} + + + {story()} + + @@ -212,7 +231,7 @@ export const DecorateWithAssetDetailsStateContext: DecoratorFn = (story) => { to: '2023-04-09T11:23:49Z', }} > - {story()} + {story()} ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx index 4c7613e843c39..49a6e1a17ac6c 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/asset_details.tsx @@ -25,17 +25,11 @@ const ContentTemplate = ({ ); }; -export const AssetDetails = ({ - tabs, - links, - renderMode, - metricAlias, - ...props -}: AssetDetailsProps) => { +export const AssetDetails = ({ tabs, links, renderMode, ...props }: AssetDetailsProps) => { return ( - + diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx index f1ccf0ef31b2b..e356eb009a1c4 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx @@ -4,11 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useMemo } from 'react'; - +import React, { useCallback } from 'react'; import type { LensConfig, LensDataviewDataset } from '@kbn/lens-embeddable-utils/config_builder'; import type { TimeRange } from '@kbn/es-query'; -import { useDataView } from '../../../hooks/use_data_view'; +import useAsync from 'react-use/lib/useAsync'; +import { resolveDataView } from '../../../utils/data_view'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { METRIC_CHART_HEIGHT } from '../../../common/visualizations/constants'; import { buildCombinedAssetFilter } from '../../../utils/filters/build'; import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../lens'; @@ -27,17 +28,24 @@ export type ChartProps = LensConfig & export const Chart = ({ id, queryField, overrides, dateRange, assetId, ...props }: ChartProps) => { const { setDateRange } = useDatePickerContext(); const { searchSessionId } = useLoadingStateContext(); - const { dataView } = useDataView({ index: (props.dataset as LensDataviewDataset)?.index }); + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); + + const { value: filters = [] } = useAsync(async () => { + const resolvedDataView = await resolveDataView({ + dataViewId: (props.dataset as LensDataviewDataset)?.index, + dataViewsService: dataViews, + }); - const filters = useMemo(() => { return [ buildCombinedAssetFilter({ field: queryField, values: [assetId], - dataView, + dataView: resolvedDataView.dataViewReference, }), ]; - }, [assetId, dataView, queryField]); + }, [assetId, dataViews, props.dataset, queryField]); const handleBrushEnd = useCallback( ({ range, preventDefault }: BrushEndArgs) => { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx index d1294f1b513e6..6cef28056d9a6 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/context_providers.tsx @@ -15,7 +15,7 @@ import { AssetDetailsProps } from './types'; export const ContextProviders = ({ children, ...props -}: Omit & { +}: Omit & { children: React.ReactNode; }) => { const { diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts index 8207343f2bc4d..6ef5ab61f517d 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_data_views.ts @@ -9,15 +9,14 @@ import useAsync from 'react-use/lib/useAsync'; import createContainer from 'constate'; import { i18n } from '@kbn/i18n'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { useLogViewReference } from '../../../hooks/use_log_view_reference'; -import { useDataView } from '../../../hooks/use_data_view'; import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props'; -const useDataViews = ({ metricAlias }: { metricAlias: string }) => { +const useDataViews = () => { const { asset } = useAssetDetailsRenderPropsContext(); - const { dataView: metricsDataView, loading: metricsDataViewLoading } = useDataView({ - index: metricAlias, - }); + const { metricsView, loading: metricsDataViewLoading } = useMetricsDataViewContext(); + const { logViewReference, getLogsDataView, @@ -39,7 +38,7 @@ const useDataViews = ({ metricAlias }: { metricAlias: string }) => { ); return { - metrics: { dataView: metricsDataView, loading: metricsDataViewLoading }, + metrics: { dataView: metricsView?.dataViewReference, loading: metricsDataViewLoading }, logs: { dataView: logsDataView, reference: logViewReference, diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts index 5ec0d38739ba4..6ddff690c3818 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_process_list.ts @@ -14,7 +14,7 @@ import { BehaviorSubject } from 'rxjs'; import { ProcessListAPIResponse, ProcessListAPIResponseRT } from '../../../../common/http_api'; import { throwErrors, createPlainError } from '../../../../common/runtime_types'; import { useHTTPRequest } from '../../../hooks/use_http_request'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; export interface SortBy { name: string; @@ -28,8 +28,7 @@ export function useProcessList( searchFilter: object, request$?: BehaviorSubject<(() => Promise) | undefined> ) { - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern().title; + const { metricsView } = useMetricsDataViewContext(); const decodeResponse = (response: any) => { return pipe( @@ -51,7 +50,7 @@ export function useProcessList( 'POST', JSON.stringify({ hostTerm, - indexPattern, + indexPattern: metricsView?.indices, to, sortBy: parsedSortBy, searchFilter, @@ -80,9 +79,8 @@ export function useProcessList( function useProcessListParams(props: { hostTerm: Record; to: number }) { const { hostTerm, to } = props; - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern().title; - return { hostTerm, indexPattern, to }; + const { metricsView } = useMetricsDataViewContext(); + return { hostTerm, indexPattern: metricsView?.indices, to }; } const ProcessListContext = createContainter(useProcessListParams); export const [ProcessListContextProvider, useProcessListContext] = ProcessListContext; diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts index 35eea0faab44b..afbf4fd35756a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/types.ts @@ -80,9 +80,6 @@ export interface AssetDetailsProps { overrides?: OverridableTabState; renderMode: RenderMode; links?: LinkOptions[]; - // This is temporary. Once we start using the asset details in other plugins, - // It will have to retrieve the metricAlias internally rather than receive it via props - metricAlias: string; } export type TabsStateChangeFn = (state: TabState) => void; diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx index dac65711e592f..bdace63850f36 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/anomaly_detection_flyout.tsx @@ -8,7 +8,7 @@ import React, { useState, useCallback } from 'react'; import { EuiHeaderLink, EuiFlyout } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { FlyoutHome } from './flyout_home'; import { JobSetupScreen } from './job_setup_screen'; import { useInfraMLCapabilities } from '../../../containers/ml/infra_ml_capabilities'; @@ -17,8 +17,8 @@ import { MetricK8sModuleProvider } from '../../../containers/ml/modules/metrics_ import { useActiveKibanaSpace } from '../../../hooks/use_kibana_space'; export const AnomalyDetectionFlyout = ({ - hideJobType, - hideSelectGroup, + hideJobType = false, + hideSelectGroup = false, }: { hideJobType?: boolean; hideSelectGroup?: boolean; @@ -27,7 +27,7 @@ export const AnomalyDetectionFlyout = ({ const [showFlyout, setShowFlyout] = useState(false); const [screenName, setScreenName] = useState<'home' | 'setup'>('home'); const [screenParams, setScreenParams] = useState(null); - const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { space } = useActiveKibanaSpace(); @@ -48,7 +48,7 @@ export const AnomalyDetectionFlyout = ({ setShowFlyout(false); }, []); - if (source?.configuration.metricAlias == null || space == null) { + if (!metricsView?.indices || !space) { return null; } @@ -68,12 +68,12 @@ export const AnomalyDetectionFlyout = ({ {showFlyout && ( diff --git a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx index c17b73703fe97..52150d0f9066a 100644 --- a/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/observability_solution/infra/public/components/ml/anomaly_detection/job_setup_screen.tsx @@ -31,7 +31,7 @@ import moment, { Moment } from 'moment'; import { i18n } from '@kbn/i18n'; import { FeatureFeedbackButton, useUiTracker } from '@kbn/observability-shared-plugin/public'; import { css } from '@emotion/react'; -import { useSourceContext } from '../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../containers/metrics_source'; import { useMetricHostsModuleContext } from '../../../containers/ml/modules/metrics_hosts/module'; import { useMetricK8sModuleContext } from '../../../containers/ml/modules/metrics_k8s/module'; import { FixedDatePicker } from '../../fixed_datepicker'; @@ -54,10 +54,10 @@ export const JobSetupScreen = (props: Props) => { const [partitionField, setPartitionField] = useState(null); const host = useMetricHostsModuleContext(); const kubernetes = useMetricK8sModuleContext(); + const { metricsView } = useMetricsDataViewContext(); const [filter, setFilter] = useState(''); const [filterQuery, setFilterQuery] = useState(''); const trackMetric = useUiTracker({ app: 'infra_metrics' }); - const { createDerivedIndexPattern } = useSourceContext(); const { kibanaVersion, isCloudEnv, isServerlessEnv } = useContext(KibanaEnvironmentContext); const { euiTheme } = useEuiTheme(); @@ -95,11 +95,6 @@ export const JobSetupScreen = (props: Props) => { } }, [props.jobType, kubernetes.jobSummaries, host.jobSummaries]); - const derivedIndexPattern = useMemo( - () => createDerivedIndexPattern(), - [createDerivedIndexPattern] - ); - const updateStart = useCallback((date: Moment) => { setStartDate(date); }, []); @@ -135,9 +130,9 @@ export const JobSetupScreen = (props: Props) => { const onFilterChange = useCallback( (f: string) => { setFilter(f || ''); - setFilterQuery(convertKueryToElasticSearchQuery(f, derivedIndexPattern) || ''); + setFilterQuery(convertKueryToElasticSearchQuery(f, metricsView?.dataViewReference) || ''); }, - [derivedIndexPattern] + [metricsView?.dataViewReference] ); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -323,7 +318,7 @@ export const JobSetupScreen = (props: Props) => { selectedOptions={ partitionField ? partitionField.map((p) => ({ label: p })) : undefined } - options={derivedIndexPattern.fields + options={(metricsView?.fields ?? []) .filter((f) => f.aggregatable && f.type === 'string') .map((f) => ({ label: f.name }))} onChange={onPartitionFieldChange} @@ -358,7 +353,6 @@ export const JobSetupScreen = (props: Props) => { } > { + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); + + const { source } = useSourceContext(); + + const [state, refetch] = useAsyncFn(async () => { + const indexPattern = source?.configuration.metricAlias; + if (!indexPattern) { + return Promise.resolve(undefined); + } + + return resolveAdHocDataView({ + dataViewsService: dataViews, + dataViewId: indexPattern, + attributes: { + name: DEFAULT_METRICS_VIEW_ATTRIBUTES.name, + timeFieldName: DEFAULT_METRICS_VIEW_ATTRIBUTES.timeFieldName, + }, + }); + }, [dataViews, source?.configuration.metricAlias]); + + useEffect(() => { + refetch(); + }, [refetch]); + + const { value: metricsView, error, loading } = state; + + return { + metricsView, + loading, + error, + refetch, + }; +}; + +export const [MetricsDataViewProvider, useMetricsDataViewContext] = + createContainer(useMetricsDataView); diff --git a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx b/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx index 336b07b698675..77024a6165502 100644 --- a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx +++ b/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/source.tsx @@ -19,44 +19,32 @@ import type { import { useTrackedPromise } from '../../utils/use_tracked_promise'; import { MissingHttpClientException } from './source_errors'; import { useSourceNotifier } from './notifications'; +import { MetricsDataViewProvider } from './metrics_view'; -export const pickIndexPattern = ( - source: MetricsSourceConfiguration | undefined, - type: 'metrics' -) => { - if (!source) { - return 'unknown-index'; - } - if (type === 'metrics') { - return source.configuration.metricAlias; - } - return `${source.configuration.metricAlias}`; -}; +const API_URL = `/api/metrics/source`; -export const useSource = ({ sourceId }: { sourceId: string }) => { +export const useSourceFetcher = ({ sourceId }: { sourceId: string }) => { + const [source, setSource] = useState(undefined); const { services: { http, telemetry }, } = useKibanaContextForPlugin(); - const notify = useSourceNotifier(); - const fetchService = http; - const API_URL = `/api/metrics/source/${sourceId}`; - - const [source, setSource] = useState(undefined); - const [loadSourceRequest, loadSource] = useTrackedPromise( { cancelPreviousOn: 'resolution', createPromise: async () => { - if (!fetchService) { + if (!http) { throw new MissingHttpClientException(); } const start = performance.now(); - const response = await fetchService.fetch(API_URL, { - method: 'GET', - }); + const response = await http.fetch( + `${API_URL}/${sourceId}`, + { + method: 'GET', + } + ); telemetry?.reportPerformanceMetricEvent( 'infra_source_load', performance.now() - start, @@ -71,17 +59,17 @@ export const useSource = ({ sourceId }: { sourceId: string }) => { } }, }, - [fetchService, sourceId] + [http, sourceId] ); - const [createSourceConfigurationRequest, createSourceConfiguration] = useTrackedPromise( + const [persistSourceConfigurationRequest, persistSourceConfiguration] = useTrackedPromise( { createPromise: async (sourceProperties: PartialMetricsSourceConfigurationProperties) => { - if (!fetchService) { + if (!http) { throw new MissingHttpClientException(); } - return await fetchService.patch(API_URL, { + return await http.patch(`${API_URL}/${sourceId}`, { method: 'PATCH', body: JSON.stringify(sourceProperties), }); @@ -96,60 +84,60 @@ export const useSource = ({ sourceId }: { sourceId: string }) => { notify.updateFailure((error as IHttpFetchError<{ message: string }>).body?.message); }, }, - [fetchService, sourceId] + [http, sourceId] ); useEffect(() => { loadSource(); }, [loadSource, sourceId]); - const createDerivedIndexPattern = () => { - return { - fields: source?.status ? source.status.indexFields : [], - title: pickIndexPattern(source, 'metrics'), - }; + const error = loadSourceRequest.state === 'rejected' ? `${loadSourceRequest.value}` : undefined; + const isLoading = + loadSourceRequest.state === 'uninitialized' || + loadSourceRequest.state === 'pending' || + persistSourceConfigurationRequest.state === 'pending'; + + return { + error, + loadSource, + isLoading, + source, + persistSourceConfiguration, }; +}; - const hasFailedLoadingSource = loadSourceRequest.state === 'rejected'; - const isUninitialized = loadSourceRequest.state === 'uninitialized'; - const isLoadingSource = loadSourceRequest.state === 'pending'; - const isLoading = isLoadingSource || createSourceConfigurationRequest.state === 'pending'; +export const useSource = ({ sourceId }: { sourceId: string }) => { + const { persistSourceConfiguration, source, error, isLoading, loadSource } = useSourceFetcher({ + sourceId, + }); const sourceExists = source ? !!source.version : undefined; const metricIndicesExist = Boolean(source?.status?.metricIndicesExist); - const version = source?.version; - return { - createSourceConfiguration, - createDerivedIndexPattern, isLoading, - isLoadingSource, - isUninitialized, - hasFailedLoadingSource, + error, loadSource, - loadSourceRequest, - loadSourceFailureMessage: hasFailedLoadingSource ? `${loadSourceRequest.value}` : undefined, metricIndicesExist, source, sourceExists, sourceId, - updateSourceConfiguration: createSourceConfiguration, - version, + persistSourceConfiguration, }; }; export const [SourceProvider, useSourceContext] = createContainer(useSource); export const withSourceProvider = - (Component: React.FunctionComponent) => + (Component: React.FC) => (sourceId = 'default') => { return function ComponentWithSourceProvider(props: ComponentProps) { return ( - {/* @ts-expect-error upgrade typescript v4.9.5*/} - + + + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/types.ts b/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/types.ts deleted file mode 100644 index 28b022347604b..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/containers/metrics_source/types.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { MetricsSourceStatus } from '../../../common/metrics_sources'; - -export interface DerivedIndexPattern { - fields: MetricsSourceStatus['indexFields']; - title: string; -} - -export type CreateDerivedIndexPattern = () => DerivedIndexPattern; diff --git a/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx b/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx index f9177a23d1e51..4ee3a5e144727 100644 --- a/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx +++ b/x-pack/plugins/observability_solution/infra/public/containers/with_kuery_autocompletion.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { DataViewBase } from '@kbn/es-query'; import { withKibana, KibanaReactContextValue, @@ -24,7 +23,7 @@ interface WithKueryAutocompletionLifecycleProps { loadSuggestions: (expression: string, cursorPosition: number, maxSuggestions?: number) => void; suggestions: QuerySuggestion[]; }>; - indexPattern: DataViewBase; + dataView?: DataView; } interface WithKueryAutocompletionLifecycleState { @@ -62,7 +61,7 @@ class WithKueryAutocompletionComponent extends React.Component< maxSuggestions?: number, transformSuggestions?: (s: QuerySuggestion[]) => QuerySuggestion[] ) => { - const { indexPattern } = this.props; + const { dataView } = this.props; const language = 'kuery'; const hasQuerySuggestions = this.props.kibana.services.unifiedSearch.autocomplete.hasQuerySuggestions(language); @@ -79,15 +78,16 @@ class WithKueryAutocompletionComponent extends React.Component< suggestions: [], }); - const suggestions = - (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ - language, - query: expression, - selectionStart: cursorPosition, - selectionEnd: cursorPosition, - indexPatterns: [indexPattern as DataView], - boolFilter: [], - })) || []; + const suggestions = dataView + ? (await this.props.kibana.services.unifiedSearch.autocomplete.getQuerySuggestions({ + language, + query: expression, + selectionStart: cursorPosition, + selectionEnd: cursorPosition, + indexPatterns: [dataView], + boolFilter: [], + })) ?? [] + : []; const transformedSuggestions = transformSuggestions ? transformSuggestions(suggestions) diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts deleted file mode 100644 index 157a594498273..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts +++ /dev/null @@ -1,79 +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 { useDataView } from './use_data_view'; -import { renderHook } from '@testing-library/react-hooks'; -import { type KibanaReactContextValue, useKibana } from '@kbn/kibana-react-plugin/public'; -import { coreMock } from '@kbn/core/public/mocks'; -import type { DataView, DataViewsServicePublic } from '@kbn/data-views-plugin/public'; -import type { InfraClientStartDeps } from '../types'; -import { CoreStart } from '@kbn/core/public'; - -jest.mock('@kbn/kibana-react-plugin/public'); - -let dataViewMock: jest.Mocked; -const useKibanaMock = useKibana as jest.MockedFunction; - -const mockUseKibana = () => { - useKibanaMock.mockReturnValue({ - services: { - ...coreMock.createStart(), - dataViews: dataViewMock, - } as Partial & Partial, - } as unknown as KibanaReactContextValue & Partial>); -}; - -const mockDataView = { - id: 'mock-id', - title: 'mock-title', - timeFieldName: 'mock-time-field-name', - isPersisted: () => false, - getName: () => 'mock-data-view', - toSpec: () => ({}), -} as jest.Mocked; - -describe('useDataView hook', () => { - beforeEach(() => { - dataViewMock = { - create: jest.fn().mockImplementation(() => Promise.resolve(mockDataView)), - get: jest.fn().mockImplementation(() => Promise.reject(new Error('Data view not found'))), - } as Partial as jest.Mocked; - - mockUseKibana(); - }); - - it('should create a new ad-hoc data view', async () => { - const { result, waitForNextUpdate } = renderHook(() => useDataView({ index: 'test' })); - - await waitForNextUpdate(); - expect(result.current.loading).toEqual(false); - expect(result.current.error).toBeUndefined(); - expect(result.current.dataView).toEqual(mockDataView); - }); - - it('should create a dataview with unique id for metricAlias metrics', async () => { - const { waitForNextUpdate } = renderHook(() => useDataView({ index: 'metrics' })); - - await waitForNextUpdate(); - expect(dataViewMock.create).toHaveBeenCalledWith({ - id: 'infra_metrics_212933f0-c55e-5a36-8b13-e724aed2f66d', - timeFieldName: '@timestamp', - title: 'metrics', - }); - }); - - it('should create a dataview with unique id for metricAlias remote-metrics', async () => { - const { waitForNextUpdate } = renderHook(() => useDataView({ index: 'remote-metrics' })); - - await waitForNextUpdate(); - expect(dataViewMock.create).toHaveBeenCalledWith({ - id: 'infra_metrics_e40bb657-0351-548e-8e73-093851d9bb6e', - timeFieldName: '@timestamp', - title: 'remote-metrics', - }); - }); -}); diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts deleted file mode 100644 index 5ad22e76d2622..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.ts +++ /dev/null @@ -1,49 +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 { v5 as uuidv5 } from 'uuid'; -import useAsyncRetry from 'react-use/lib/useAsyncRetry'; -import { useKibanaContextForPlugin } from './use_kibana'; - -export const TIMESTAMP_FIELD = '@timestamp'; -export const DATA_VIEW_PREFIX = 'infra_metrics'; - -export const generateDataViewId = (index: string) => { - // generates a unique but the same uuid as long as the index pattern doesn't change - return `${DATA_VIEW_PREFIX}_${uuidv5(index, uuidv5.DNS)}`; -}; - -export const useDataView = ({ index }: { index?: string }) => { - const { - services: { dataViews }, - } = useKibanaContextForPlugin(); - - const state = useAsyncRetry(async () => { - if (!index) { - return Promise.resolve(undefined); - } - - return dataViews.get(index, false).catch(() => - // if data view doesn't exist, create an ad-hoc one - dataViews.create({ - id: generateDataViewId(index), - title: index, - timeFieldName: TIMESTAMP_FIELD, - }) - ); - }, [index]); - - const { value: dataView, loading, error, retry } = state; - - return { - index, - dataView, - loading, - retry, - error, - }; -}; diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts index 35bb5a154379d..a8856e9742e63 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_inventory_views.ts @@ -47,7 +47,7 @@ export const useInventoryViews = (): UseInventoryViewsResult => { const trackMetric = useUiTracker({ app: 'infra_metrics' }); const queryClient = useQueryClient(); - const { source, updateSourceConfiguration } = useSourceContext(); + const { source, persistSourceConfiguration } = useSourceContext(); const defaultViewId = source?.configuration.inventoryDefaultView ?? '0'; @@ -93,7 +93,7 @@ export const useInventoryViews = (): UseInventoryViewsResult => { string, MutationContext >({ - mutationFn: (id) => updateSourceConfiguration({ inventoryDefaultView: id }), + mutationFn: (id) => persistSourceConfiguration({ inventoryDefaultView: id }), /** * To provide a quick feedback, we perform an optimistic update on the list * when updating the default view. diff --git a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts index 7e0eb3bd387a1..fc66789c22962 100644 --- a/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts +++ b/x-pack/plugins/observability_solution/infra/public/hooks/use_metrics_explorer_views.ts @@ -52,7 +52,7 @@ export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => { const trackMetric = useUiTracker({ app: 'infra_metrics' }); const queryClient = useQueryClient(); - const { source, updateSourceConfiguration } = useSourceContext(); + const { source, persistSourceConfiguration } = useSourceContext(); const defaultViewId = source?.configuration.metricsExplorerDefaultView ?? '0'; @@ -98,7 +98,7 @@ export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => { string, MutationContext >({ - mutationFn: (id) => updateSourceConfiguration({ metricsExplorerDefaultView: id }), + mutationFn: (id) => persistSourceConfiguration({ metricsExplorerDefaultView: id }), /** * To provide a quick feedback, we perform an optimistic update on the list * when updating the default view. diff --git a/x-pack/plugins/observability_solution/infra/public/pages/404.tsx b/x-pack/plugins/observability_solution/infra/public/pages/404.tsx index e0a87b6af4235..8f4333a6b6e26 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/404.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/404.tsx @@ -7,21 +7,29 @@ import React from 'react'; import { NotFoundPrompt } from '@kbn/shared-ux-prompt-not-found'; -import { MetricsPageTemplate } from './metrics/page_template'; +import { useKibanaContextForPlugin } from '../hooks/use_kibana'; interface NotFoundPageProps { title: string; } export const NotFoundPage = ({ title }: NotFoundPageProps) => { + const { + services: { + observabilityShared: { + navigation: { PageTemplate }, + }, + }, + } = useKibanaContextForPlugin(); + return ( - - + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx index ad7b3e6e509fd..b80708c91c2cd 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/link_to/redirect_to_host_detail_via_ip.tsx @@ -14,7 +14,7 @@ import { ASSET_DETAILS_LOCATOR_ID } from '@kbn/observability-shared-plugin/publi import { useHostIpToName } from './use_host_ip_to_name'; import { LoadingPage } from '../../components/loading_page'; import { Error } from '../error'; -import { useSourceContext } from '../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../containers/metrics_source'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { getSearchParams } from './redirect_to_node_detail'; @@ -28,16 +28,13 @@ export const RedirectToHostDetailViaIP = ({ }, location, }: RedirectToHostDetailType) => { - const { source } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { services: { share }, } = useKibanaContextForPlugin(); const baseLocator = share.url.locators.get(ASSET_DETAILS_LOCATOR_ID); - const { error, name } = useHostIpToName( - hostIp, - (source && source.configuration && source.configuration.metricAlias) || null - ); + const { error, name } = useHostIpToName(hostIp, (metricsView && metricsView.indices) || null); useEffect(() => { if (name) { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx index d7e36d950d4ca..694196cf1a0ac 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/host_details_flyout/flyout_wrapper.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import type { HostNodeRow } from '../../hooks/use_hosts_table'; import { AssetDetails } from '../../../../../components/asset_details'; @@ -18,10 +17,9 @@ export interface Props { } export const FlyoutWrapper = ({ node: { name }, closeFlyout }: Props) => { - const { source } = useSourceContext(); const { parsedDateRange } = useUnifiedSearchContext(); - return source ? ( + return ( { mode: 'flyout', closeFlyout, }} - metricAlias={source.configuration.metricAlias} /> - ) : null; + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx index 5b5fab795ff81..044592e338789 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/hosts_container.tsx @@ -7,45 +7,12 @@ import React from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { InfraLoadingPanel } from '../../../../components/loading'; -import { useMetricsDataViewContext } from '../hooks/use_metrics_data_view'; import { UnifiedSearchBar } from './search_bar/unified_search_bar'; import { HostsContent } from './hosts_content'; -import { ErrorCallout } from './error_callout'; import { UnifiedSearchProvider } from '../hooks/use_unified_search'; export const HostContainer = () => { - const { dataView, loading, error, metricAlias, retry } = useMetricsDataViewContext(); - - const isLoading = loading || !dataView; - if (isLoading && !error) { - return ( - - ); - } - - return error ? ( - - ) : ( + return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx index 9fd8f40fa08c7..9b349441c5876 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/kpis/kpi_charts.tsx @@ -12,13 +12,13 @@ import { useUnifiedSearchContext } from '../../hooks/use_unified_search'; import { useHostsViewContext } from '../../hooks/use_hosts_view'; import { useHostCountContext } from '../../hooks/use_host_count'; import { useAfterLoadedState } from '../../hooks/use_after_loaded_state'; -import { useMetricsDataViewContext } from '../../hooks/use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../../containers/metrics_source'; export const KpiCharts = () => { const { searchCriteria } = useUnifiedSearchContext(); const { hostNodes, loading: hostsLoading, searchSessionId } = useHostsViewContext(); const { isRequestRunning: hostCountLoading, data: hostCountData } = useHostCountContext(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const shouldUseSearchCriteria = hostNodes.length === 0; const loading = hostsLoading || hostCountLoading; @@ -29,7 +29,7 @@ export const KpiCharts = () => { buildCombinedAssetFilter({ field: 'host.name', values: hostNodes.map((p) => p.name), - dataView, + dataView: metricsView?.dataViewReference, }), ]; @@ -71,7 +71,7 @@ export const KpiCharts = () => { return ( { const { services: { unifiedSearch, application }, } = useKibanaContextForPlugin(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { searchCriteria, onSubmit } = useUnifiedSearchContext(); const { SearchBar } = unifiedSearch.ui; @@ -52,7 +52,7 @@ export const UnifiedSearchBar = () => { 0.5)', })} @@ -75,7 +75,7 @@ export const UnifiedSearchBar = () => { { const { searchCriteria } = useUnifiedSearchContext(); const { loading, searchSessionId } = useHostsViewContext(); const { currentPage } = useHostsTableContext(); - const { dataView } = useDataView({ index: (chartProps.dataset as LensDataviewDataset)?.index }); + const { + services: { dataViews }, + } = useKibanaContextForPlugin(); const shouldUseSearchCriteria = currentPage.length === 0; @@ -36,22 +41,27 @@ export const Chart = ({ id, ...chartProps }: ChartProps) => { searchSessionId, }); - const filters = useMemo(() => { + const { value: filters = [] } = useAsync(async () => { + const resolvedDataView = await resolveDataView({ + dataViewId: (chartProps.dataset as LensDataviewDataset)?.index, + dataViewsService: dataViews, + }); + return shouldUseSearchCriteria ? [...searchCriteria.filters, ...(searchCriteria.panelFilters ?? [])] : [ buildCombinedAssetFilter({ - field: 'host.name', + field: HOST_NAME_FIELD, values: currentPage.map((p) => p.name), - dataView, + dataView: resolvedDataView.dataViewReference, }), ]; }, [ - shouldUseSearchCriteria, + currentPage, + dataViews, + chartProps.dataset, searchCriteria.filters, searchCriteria.panelFilters, - currentPage, - dataView, ]); return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx index aafe961b7dc0b..49e833a74a33e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/components/tabs/metrics/metrics_grid.tsx @@ -10,13 +10,12 @@ import { EuiFlexGrid, EuiFlexItem, EuiText, EuiFlexGroup, EuiSpacer } from '@ela import { HostMetricsExplanationContent } from '../../../../../../components/lens'; import { Chart } from './chart'; import { Popover } from '../../common/popover'; -import { useMetricsDataViewContext } from '../../../hooks/use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../../../containers/metrics_source'; import { useMetricsCharts } from '../../../hooks/use_metrics_charts'; export const MetricsGrid = () => { - const { dataView } = useMetricsDataViewContext(); - - const charts = useMetricsCharts({ dataViewId: dataView?.id }); + const { metricsView } = useMetricsDataViewContext(); + const charts = useMetricsCharts({ dataViewId: metricsView?.dataViewReference.id }); return ( <> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts index c68ec312abab1..d3592cc050884 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_host_count.ts @@ -12,15 +12,16 @@ import { useCallback, useEffect, useMemo } from 'react'; import { catchError, map, Observable, of, startWith, tap } from 'rxjs'; import createContainer from 'constate'; import type { QueryDslQueryContainer, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { HOST_NAME_FIELD, TIMESTAMP_FIELD } from '../../../../../common/constants'; import type { ITelemetryClient } from '../../../../services/telemetry'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { useDataSearch, useLatestPartialDataSearchResponse } from '../../../../utils/data_search'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { useUnifiedSearchContext } from './use_unified_search'; export const useHostCount = () => { - const { dataView, metricAlias } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { services: { telemetry }, } = useKibanaContextForPlugin(); @@ -37,12 +38,12 @@ export const useHostCount = () => { ...query.bool.filter, { exists: { - field: 'host.name', + field: HOST_NAME_FIELD, }, }, { range: { - [dataView?.timeFieldName ?? '@timestamp']: { + [metricsView?.timeFieldName ?? TIMESTAMP_FIELD]: { gte: searchCriteria.dateRange.from, lte: searchCriteria.dateRange.to, }, @@ -57,7 +58,7 @@ export const useHostCount = () => { params: { allow_no_indices: true, ignore_unavailable: true, - index: metricAlias, + index: metricsView?.indices, size: 0, track_total_hits: false, body: { @@ -65,7 +66,7 @@ export const useHostCount = () => { aggs: { count: { cardinality: { - field: 'host.name', + field: HOST_NAME_FIELD, }, }, }, @@ -76,8 +77,8 @@ export const useHostCount = () => { }; }, [ buildQuery, - dataView?.timeFieldName, - metricAlias, + metricsView?.indices, + metricsView?.timeFieldName, searchCriteria.dateRange.from, searchCriteria.dateRange.to, ]), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts index 9534406d5dcfa..5df80a9e3634d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.test.ts @@ -11,11 +11,13 @@ import { InfraAssetMetricsItem } from '../../../../../common/http_api'; import * as useUnifiedSearchHooks from './use_unified_search'; import * as useHostsViewHooks from './use_hosts_view'; import * as useKibanaContextForPluginHook from '../../../../hooks/use_kibana'; -import * as useMetricsDataViewHooks from './use_metrics_data_view'; +import * as useMetricsDataViewHooks from '../../../../containers/metrics_source'; +import type { DataView } from '@kbn/data-views-plugin/common'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; jest.mock('./use_unified_search'); jest.mock('./use_hosts_view'); -jest.mock('./use_metrics_data_view'); +jest.mock('../../../../containers/metrics_source'); jest.mock('../../../../hooks/use_kibana'); const mockUseUnifiedSearchContext = @@ -138,7 +140,15 @@ describe('useHostTable hook', () => { } as ReturnType); mockUseMetricsDataViewContext.mockReturnValue({ - dataView: { id: 'default' }, + metricsView: { + indices: 'metrics-*', + fields: [], + timeFieldName: TIMESTAMP_FIELD, + dataViewReference: { id: 'default' } as DataView, + }, + error: undefined, + loading: false, + refetch: jest.fn(), } as ReturnType); mockUseKibanaContextForPlugin.mockReturnValue({ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx index 0cf70b4b4182b..cdafb71784ab9 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_hosts_table.tsx @@ -15,6 +15,7 @@ import { CloudProvider } from '@kbn/custom-icons'; import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; import { EuiToolTip } from '@elastic/eui'; import { EuiBadge } from '@elastic/eui'; +import { HOST_NAME_FIELD } from '../../../../../common/constants'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter'; import { EntryTitle } from '../components/table/entry_title'; @@ -25,7 +26,7 @@ import type { } from '../../../../../common/http_api'; import { Sorting, useHostsTableUrlState } from './use_hosts_table_url_state'; import { useHostsViewContext } from './use_hosts_view'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { ColumnHeader } from '../components/table/column_header'; import { TABLE_COLUMN_LABEL, TABLE_CONTENT_LABEL } from '../translations'; import { METRICS_TOOLTIP } from '../../../../common/visualizations'; @@ -138,7 +139,7 @@ export const useHostsTable = () => { }, }, } = useKibanaContextForPlugin(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const closeFlyout = useCallback(() => setProperties({ detailsItemId: null }), [setProperties]); @@ -152,14 +153,14 @@ export const useHostsTable = () => { } const selectedHostNames = selectedItems.map(({ name }) => name); const newFilter = buildCombinedAssetFilter({ - field: 'host.name', + field: HOST_NAME_FIELD, values: selectedHostNames, - dataView, + dataView: metricsView?.dataViewReference, }); filterManagerService.addFilters(newFilter); setSelectedItems([]); - }, [dataView, filterManagerService, selectedItems]); + }, [filterManagerService, metricsView?.dataViewReference, selectedItems]); const reportHostEntryClick = useCallback( ({ name, cloudProvider }: HostNodeRow['title']) => { diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts deleted file mode 100644 index 4c62bc58f00a9..0000000000000 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_metrics_data_view.ts +++ /dev/null @@ -1,26 +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 createContainer from 'constate'; -import { useDataView } from '../../../../hooks/use_data_view'; - -export const useMetricsDataView = ({ metricAlias }: { metricAlias: string }) => { - const { dataView, loading, retry, error } = useDataView({ - index: metricAlias, - }); - - return { - metricAlias, - dataView, - loading, - retry, - error, - }; -}; - -export const MetricsDataView = createContainer(useMetricsDataView); -export const [MetricsDataViewProvider, useMetricsDataViewContext] = MetricsDataView; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts index ac6e1d9a5f9e7..4063db1e41727 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_unified_search.ts @@ -15,7 +15,7 @@ import { parseDateRange } from '../../../../utils/datemath'; import { useKibanaQuerySettings } from '../../../../utils/use_kibana_query_settings'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { telemetryTimeRangeFormatter } from '../../../../../common/formatters/telemetry_time_range'; -import { useMetricsDataViewContext } from './use_metrics_data_view'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { HostsSearchPayload, useHostsUrlState, @@ -52,7 +52,7 @@ const getDefaultTimestamps = () => { export const useUnifiedSearch = () => { const [error, setError] = useState(null); const [searchCriteria, setSearch] = useHostsUrlState(); - const { dataView } = useMetricsDataViewContext(); + const { metricsView } = useMetricsDataViewContext(); const { services } = useKibanaContextForPlugin(); const kibanaQuerySettings = useKibanaQuerySettings(); @@ -116,13 +116,13 @@ export const useUnifiedSearch = () => { const buildQuery = useCallback(() => { return buildEsQuery( - dataView, + metricsView?.dataViewReference, searchCriteria.query, [...searchCriteria.filters, ...searchCriteria.panelFilters], kibanaQuerySettings ); }, [ - dataView, + metricsView?.dataViewReference, searchCriteria.query, searchCriteria.filters, searchCriteria.panelFilters, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx index 80dc5a3977c75..998262283cf9a 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/index.tsx @@ -12,23 +12,17 @@ import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { useKibanaEnvironmentContext } from '../../../hooks/use_kibana'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { MetricsPageTemplate } from '../page_template'; import { hostsTitle } from '../../../translations'; -import { MetricsDataViewProvider } from './hooks/use_metrics_data_view'; import { fullHeightContentStyles } from '../../../page_template.styles'; import { HostContainer } from './components/hosts_container'; import { BetaBadge } from '../../../components/beta_badge'; -import { NoRemoteCluster } from '../../../components/empty_states'; const HOSTS_FEEDBACK_LINK = 'https://docs.google.com/forms/d/e/1FAIpQLScRHG8TIVb1Oq8ZhD4aks3P1TmgiM58TY123QpDCcBz83YC6w/viewform'; export const HostsPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); const { kibanaVersion, isCloudEnv, isServerlessEnv } = useKibanaEnvironmentContext(); useTrackPageview({ app: 'infra_metrics', path: 'hosts' }); @@ -40,28 +34,10 @@ export const HostsPage = () => { }, ]); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading && !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return (
{ }, }} > - {source && ( - - - - )} +
diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx index d87706a587b8b..8a96b1c2c6b6e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/index.tsx @@ -20,22 +20,17 @@ import { import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-shared-plugin/public'; import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; -import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; -import { MetricsExplorerOptionsContainer } from './metrics_explorer/hooks/use_metrics_explorer_options'; -import { WithMetricsExplorerOptionsUrlState } from '../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { MetricsExplorerPage } from './metrics_explorer'; import { SnapshotPage } from './inventory_view'; import { NodeDetail } from './metric_detail'; import { MetricsSettingsPage } from './settings'; -import { SourceLoadingPage } from '../../components/source_loading_page'; import { MetricsAlertDropdown } from '../../alerting/common/components/metrics_alert_dropdown'; import { AlertPrefillProvider } from '../../alerting/use_alert_prefill'; import { InfraMLCapabilitiesProvider } from '../../containers/ml/infra_ml_capabilities'; import { AnomalyDetectionFlyout } from '../../components/ml/anomaly_detection/anomaly_detection_flyout'; import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider'; -import { CreateDerivedIndexPattern, useSourceContext } from '../../containers/metrics_source'; import { NotFoundPage } from '../404'; import { ReactQueryProvider } from '../../containers/react_query_provider'; import { usePluginConfig } from '../../containers/plugin_config_context'; @@ -58,8 +53,6 @@ export const InfrastructurePage = () => { const kibana = useKibana(); - const { source, createDerivedIndexPattern } = useSourceContext(); - useReadOnlyBadge(!uiCapabilities?.infrastructure?.save); const settingsLinkProps = useLinkProps({ @@ -86,18 +79,14 @@ export const InfrastructurePage = () => { {settingsTabTitle} - + { - return ; - }} + path="/hosts" + render={() => } /> { - return ; - }} + path="/detail/host" + render={() => } /> {config.featureFlags.alertsAndRulesDropdownEnabled && ( @@ -120,24 +109,8 @@ export const InfrastructurePage = () => { {config.featureFlags.metricsExplorerEnabled && ( - ( - - - {source?.configuration ? ( - - ) : ( - - )} - - )} - /> + )} - {isHostsViewEnabled && } @@ -162,14 +135,3 @@ export const InfrastructurePage = () => { ); }; - -const PageContent = (props: { - configuration: MetricsSourceConfigurationProperties; - createDerivedIndexPattern: CreateDerivedIndexPattern; -}) => { - const { createDerivedIndexPattern, configuration } = props; - - return ( - - ); -}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx index 7386322774442..8b56c6468dc0f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/search_bar.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { useSourceContext } from '../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { AutocompleteField } from '../../../../components/autocomplete_field'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { useWaffleFiltersContext } from '../hooks/use_waffle_filters'; export const SearchBar = () => { - const { createDerivedIndexPattern } = useSourceContext(); + const { metricsView } = useMetricsDataViewContext(); const { applyFilterQueryFromKueryExpression, filterQueryDraft, @@ -21,7 +21,7 @@ export const SearchBar = () => { setFilterQueryDraftFromKueryExpression, } = useWaffleFiltersContext(); return ( - + {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( { })} suggestions={suggestions} value={filterQueryDraft ? filterQueryDraft : ''} - autoFocus={true} + autoFocus /> )} diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx index edf124978c093..cdfefd037e0c7 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/metrics_and_groupby_toolbar_items.tsx @@ -36,7 +36,6 @@ export const MetricsAndGroupByToolbarItems = (props: Props) => { <> { groupBy={props.groupBy} nodeType={props.nodeType} onChange={props.changeGroupBy} - fields={props.createDerivedIndexPattern().fields} onChangeCustomOptions={props.changeCustomOptions} customOptions={props.customOptions} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx index 1b81c3046362a..e1ff4674d9e8d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/toolbar_wrapper.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; import { fieldToName } from '../../lib/field_to_display_name'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { useWaffleOptionsContext } from '../../hooks/use_waffle_options'; import { WaffleInventorySwitcher } from '../waffle/waffle_inventory_switcher'; import { ToolbarProps } from './types'; @@ -37,14 +36,12 @@ export const ToolbarWrapper = (props: Props) => { customMetrics, changeCustomMetrics, } = useWaffleOptionsContext(); - const { createDerivedIndexPattern } = useSourceContext(); return ( {props.children({ - createDerivedIndexPattern, changeMetric, changeGroupBy, changeAccount, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts index c655cf62d503c..7989049d2c5a8 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/toolbars/types.ts @@ -10,12 +10,10 @@ import { SnapshotGroupBy, SnapshotMetricInput, } from '../../../../../../common/http_api/snapshot_api'; -import { CreateDerivedIndexPattern } from '../../../../../containers/metrics_source'; import { InfraGroupByOptions } from '../../../../../lib/lib'; import { WaffleOptionsState, WaffleSortOption } from '../../hooks/use_waffle_options'; export interface ToolbarProps extends Omit { - createDerivedIndexPattern: CreateDerivedIndexPattern; changeMetric: (payload: SnapshotMetricInput) => void; changeGroupBy: (payload: SnapshotGroupBy) => void; changeCustomOptions: (payload: InfraGroupByOptions[]) => void; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx index af87d5470c7d9..909ecdc8910b4 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/asset_details_flyout.tsx @@ -9,7 +9,6 @@ import React, { useMemo } from 'react'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import type { InfraWaffleMapOptions } from '../../../../../lib/lib'; import { AssetDetails } from '../../../../../components/asset_details'; -import { useSourceContext } from '../../../../../containers/metrics_source'; import { getAssetDetailsFlyoutTabs } from '../../../../../common/asset_details_config/asset_details_tabs'; interface Props { @@ -35,8 +34,6 @@ export const AssetDetailsFlyout = ({ refreshInterval, isAutoReloading = false, }: Props) => { - const { source } = useSourceContext(); - const dateRange = useMemo(() => { // forces relative dates when auto-refresh is active return isAutoReloading @@ -50,7 +47,7 @@ export const AssetDetailsFlyout = ({ }; }, [currentTime, isAutoReloading]); - return source ? ( + return ( - ) : null; + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx index 95625d3683cc4..cb4d1d57436be 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/custom_field_panel.tsx @@ -8,11 +8,10 @@ import { EuiButton, EuiComboBox, EuiForm, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; +import { useMetricsDataViewContext } from '../../../../../containers/metrics_source'; import { InfraGroupByOptions } from '../../../../../lib/lib'; -import { DerivedIndexPattern } from '../../../../../containers/metrics_source'; interface Props { onSubmit: (field: string) => void; - fields: DerivedIndexPattern['fields']; currentOptions: InfraGroupByOptions[]; } @@ -20,71 +19,65 @@ interface SelectedOption { label: string; } -const initialState = { - selectedOptions: [] as SelectedOption[], -}; - -type State = Readonly; +export const CustomFieldPanel = ({ onSubmit, currentOptions }: Props) => { + const { metricsView } = useMetricsDataViewContext(); + const [selectedOptions, setSelectedOptions] = React.useState([]); -export class CustomFieldPanel extends React.PureComponent { - public static displayName = 'CustomFieldPanel'; - public readonly state: State = initialState; - public render() { - const { fields, currentOptions } = this.props; - const options = fields - .filter( - (f) => - f.aggregatable && - f.type === 'string' && - !(currentOptions && currentOptions.some((o) => o.field === f.name)) - ) - .map((f) => ({ label: f.name })); - const isSubmitDisabled = !this.state.selectedOptions.length; - return ( -
- - - - - - Add - - -
- ); - } - private handleSubmit = () => { - this.props.onSubmit(this.state.selectedOptions[0].label); + const handleSubmit = () => { + onSubmit(selectedOptions[0].label); }; - private handleFieldSelection = (selectedOptions: SelectedOption[]) => { - this.setState({ selectedOptions }); + const handleFieldSelection = (newSelection: SelectedOption[]) => { + setSelectedOptions(newSelection); }; -} + + const options = (metricsView?.fields ?? []) + .filter( + (f) => + f.aggregatable && + f.type === 'string' && + !(currentOptions && currentOptions.some((o) => o.field === f.name)) + ) + .map((f) => ({ label: f.name })); + + const isSubmitDisabled = !selectedOptions.length; + return ( +
+ + + + + + {i18n.translate('xpack.infra..addButtonLabel', { defaultMessage: 'Add' })} + + +
+ ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx index c3cd96ca5ad43..7182b6f59c448 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/custom_metric_form.tsx @@ -29,7 +29,7 @@ import { SNAPSHOT_CUSTOM_AGGREGATIONS, SnapshotCustomAggregationRT, } from '../../../../../../../common/http_api/snapshot_api'; -import { DerivedIndexPattern } from '../../../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../../../containers/metrics_source'; interface SelectedOption { label: string; @@ -53,202 +53,201 @@ const AGGREGATION_LABELS = { interface Props { theme: EuiTheme | undefined; metric?: SnapshotCustomMetricInput; - fields: DerivedIndexPattern['fields']; customMetrics: SnapshotCustomMetricInput[]; onChange: (metric: SnapshotCustomMetricInput) => void; onCancel: () => void; } -export const CustomMetricForm = withTheme( - ({ theme, onCancel, fields, onChange, metric }: Props) => { - const [label, setLabel] = useState(metric ? metric.label : void 0); - const [aggregation, setAggregation] = useState( - metric ? metric.aggregation : 'avg' - ); - const [field, setField] = useState(metric ? metric.field : void 0); +export const CustomMetricForm = withTheme(({ theme, onCancel, onChange, metric }: Props) => { + const { metricsView } = useMetricsDataViewContext(); + const [label, setLabel] = useState(metric ? metric.label : void 0); + const [aggregation, setAggregation] = useState( + metric ? metric.aggregation : 'avg' + ); + const [field, setField] = useState(metric ? metric.field : void 0); - const handleSubmit = useCallback(() => { - if (metric && aggregation && field) { - onChange({ - ...metric, - label, - aggregation, - field, - }); - } else if (aggregation && field) { - const newMetric: SnapshotCustomMetricInput = { - type: 'custom', - id: uuidv4(), - label, - aggregation, - field, - }; - onChange(newMetric); - } - }, [metric, aggregation, field, onChange, label]); + const handleSubmit = useCallback(() => { + if (metric && aggregation && field) { + onChange({ + ...metric, + label, + aggregation, + field, + }); + } else if (aggregation && field) { + const newMetric: SnapshotCustomMetricInput = { + type: 'custom', + id: uuidv4(), + label, + aggregation, + field, + }; + onChange(newMetric); + } + }, [metric, aggregation, field, onChange, label]); - const handleLabelChange = useCallback( - (e) => { - setLabel(e.target.value); - }, - [setLabel] - ); + const handleLabelChange = useCallback( + (e) => { + setLabel(e.target.value); + }, + [setLabel] + ); - const handleFieldChange = useCallback( - (selectedOptions: SelectedOption[]) => { - setField(selectedOptions[0].label); - }, - [setField] - ); + const handleFieldChange = useCallback( + (selectedOptions: SelectedOption[]) => { + setField(selectedOptions[0].label); + }, + [setField] + ); - const handleAggregationChange = useCallback( - (e) => { - const value = e.target.value; - const aggValue: SnapshotCustomAggregation = SnapshotCustomAggregationRT.is(value) - ? value - : 'avg'; - setAggregation(aggValue); - }, - [setAggregation] - ); + const handleAggregationChange = useCallback( + (e) => { + const value = e.target.value; + const aggValue: SnapshotCustomAggregation = SnapshotCustomAggregationRT.is(value) + ? value + : 'avg'; + setAggregation(aggValue); + }, + [setAggregation] + ); - const fieldOptions = fields - .filter((f) => f.aggregatable && f.type === 'number' && !(field && field === f.name)) - .map((f) => ({ label: f.name })); + const fieldOptions = (metricsView?.fields ?? []) + .filter((f) => f.aggregatable && f.type === 'number' && !(field && field === f.name)) + .map((f) => ({ label: f.name })); - const aggregationOptions = SNAPSHOT_CUSTOM_AGGREGATIONS.map((k) => ({ - text: AGGREGATION_LABELS[k as SnapshotCustomAggregation], - value: k, - })); + const aggregationOptions = SNAPSHOT_CUSTOM_AGGREGATIONS.map((k) => ({ + text: AGGREGATION_LABELS[k as SnapshotCustomAggregation], + value: k, + })); - const isSubmitDisabled = !field || !aggregation; + const isSubmitDisabled = !field || !aggregation; - const title = metric - ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.edit', { - defaultMessage: 'Edit custom metric', - }) - : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.add', { - defaultMessage: 'Add custom metric', - }); + const title = metric + ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.edit', { + defaultMessage: 'Edit custom metric', + }) + : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.add', { + defaultMessage: 'Add custom metric', + }); - const titleAriaLabel = metric - ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.editAriaLabel', { - defaultMessage: 'Back to custom metrics edit mode', - }) - : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.addAriaLabel', { - defaultMessage: 'Back to metric picker', - }); + const titleAriaLabel = metric + ? i18n.translate('xpack.infra.waffle.customMetricPanelLabel.editAriaLabel', { + defaultMessage: 'Back to custom metrics edit mode', + }) + : i18n.translate('xpack.infra.waffle.customMetricPanelLabel.addAriaLabel', { + defaultMessage: 'Back to metric picker', + }); - return ( -
- - - - {title} - - -
+ + + - - - - - - - - of - - - - - - - - + +
+ + + + + + + + + {i18n.translate('xpack.infra.waffle.customMetrics.ofLabel', { + defaultMessage: 'of', + })} + + + + + + + + + + - - -
-
- - - - - - -
-
-
- ); - } -); + onChange={handleLabelChange} + /> + +
+
+ + + + + + +
+ + + ); +}); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx index cab778feda4df..48ddf2c1d8305 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/components/waffle/metric_control/index.tsx @@ -21,19 +21,16 @@ import { ModeSwitcher } from './mode_switcher'; import { MetricsEditMode } from './metrics_edit_mode'; import { CustomMetricMode } from './types'; import { DropdownButton } from '../../dropdown_button'; -import { DerivedIndexPattern } from '../../../../../../containers/metrics_source'; interface Props { options: Array<{ text: string; value: string }>; metric: SnapshotMetricInput; - fields: DerivedIndexPattern['fields']; onChange: (metric: SnapshotMetricInput) => void; onChangeCustomMetrics: (metrics: SnapshotCustomMetricInput[]) => void; customMetrics: SnapshotCustomMetricInput[]; } export const WaffleMetricControls = ({ - fields, onChange, onChangeCustomMetrics, metric, @@ -45,6 +42,7 @@ export const WaffleMetricControls = ({ const [editModeCustomMetrics, setEditModeCustomMetrics] = useState( [] ); + const [editCustomMetric, setEditCustomMetric] = useState(); const handleClose = useCallback(() => { setPopoverState(false); @@ -164,7 +162,6 @@ export const WaffleMetricControls = ({ ) : null} {mode === 'addMetric' ? ( ; nodeType: InventoryItemType; groupBy: SnapshotGroupBy; onChange: (groupBy: SnapshotGroupBy) => void; onChangeCustomOptions: (options: InfraGroupByOptions[]) => void; - fields: DerivedIndexPattern['fields']; customOptions: InfraGroupByOptions[]; } -const initialState = { - isPopoverOpen: false, -}; - -type State = Readonly; +export const WaffleGroupByControls = ({ + options, + nodeType, + groupBy, + onChange, + onChangeCustomOptions, + customOptions, +}: Props) => { + const [isPopoverOpen, { toggle: togglePopover, off: closePopover }] = useBoolean(false); -export class WaffleGroupByControls extends React.PureComponent { - public static displayName = 'WaffleGroupByControls'; - public readonly state: State = initialState; - - public render() { - const { nodeType, groupBy } = this.props; - const customOptions = this.props.customOptions.map((option) => ({ + const combinedOptions = [ + ...options, + ...customOptions.map((option) => ({ ...option, toolTipContent: option.text, - })); - const options = this.props.options.concat(customOptions); + })), + ]; - if (!options.length) { - throw Error( - i18n.translate('xpack.infra.waffle.unableToSelectGroupErrorMessage', { - defaultMessage: 'Unable to select group by options for {nodeType}', - values: { - nodeType, - }, - }) - ); - } - const isMaxGroupingsSelected = groupBy.length >= 2; - const maxGroupByTooltip = i18n.translate('xpack.infra.waffle.maxGroupByTooltip', { - defaultMessage: 'Only two groupings can be selected at a time', - }); - const panels: EuiContextMenuPanelDescriptor[] = [ - { - id: 'firstPanel', - title: i18n.translate('xpack.infra.waffle.selectTwoGroupingsTitle', { - defaultMessage: 'Select up to two groupings', - }), - items: [ - { - name: i18n.translate('xpack.infra.waffle.customGroupByOptionName', { - defaultMessage: 'Custom field', - }), - disabled: isMaxGroupingsSelected, - toolTipContent: isMaxGroupingsSelected ? maxGroupByTooltip : null, - icon: 'empty', - panel: 'customPanel', - }, - ...options.map((o) => { - const icon = groupBy.some((g) => g.field === o.field) ? 'check' : 'empty'; - const panel = { - name: o.text, - onClick: this.handleClick(o.field), - icon, - } as EuiContextMenuPanelItemDescriptor; - if (o.toolTipContent) { - panel.toolTipContent = o.toolTipContent; - } - if (isMaxGroupingsSelected && icon === 'empty') { - panel.toolTipContent = maxGroupByTooltip; - panel.disabled = true; - } - return panel; - }), - ], - }, - { - id: 'customPanel', - title: i18n.translate('xpack.infra.waffle.customGroupByPanelTitle', { - defaultMessage: 'Group By Custom Field', - }), - width: 685, - content: ( - - ), - }, - ]; - const buttonBody = - groupBy.length > 0 ? ( - groupBy - .map((g) => options.find((o) => o.field === g.field)) - .filter((o) => o != null) - // In this map the `o && o.field` is totally unnecessary but Typescript is - // too stupid to realize that the filter above prevents the next map from being null - .map((o) => ( - - {o && o.text} - - )) - ) : ( - - ); - - const button = ( - - {buttonBody} - - ); - - return ( - - - + if (!combinedOptions.length) { + throw Error( + i18n.translate('xpack.infra.waffle.unableToSelectGroupErrorMessage', { + defaultMessage: 'Unable to select group by options for {nodeType}', + values: { + nodeType, + }, + }) ); } - private handleRemove = (field: string) => () => { - const { groupBy } = this.props; - this.props.onChange(groupBy.filter((g) => g.field !== field)); - const options = this.props.customOptions.filter((g) => g.field !== field); - this.props.onChangeCustomOptions(options); + const handleRemove = (field: string) => { + onChange(groupBy.filter((g) => g.field !== field)); + onChangeCustomOptions(customOptions.filter((g) => g.field !== field)); // We need to close the panel after we rmeove the pill icon otherwise // it will remain open because the click is still captured by the EuiFilterButton - setTimeout(() => this.handleClose()); - }; - - private handleClose = () => { - this.setState({ isPopoverOpen: false }); + setTimeout(() => closePopover()); }; - private handleToggle = () => { - this.setState((state) => ({ isPopoverOpen: !state.isPopoverOpen })); - }; - - private handleCustomField = (field: string) => { - const options = [ - ...this.props.customOptions, + const handleCustomField = (field: string) => { + onChangeCustomOptions([ + ...customOptions, { text: field, field, }, - ]; - this.props.onChangeCustomOptions(options); - const fn = this.handleClick(field); + ]); + const fn = handleClick(field); fn(); }; - private handleClick = (field: string) => () => { - const { groupBy } = this.props; + const handleClick = (field: string) => () => { if (groupBy.some((g) => g.field === field)) { - this.handleRemove(field)(); - } else if (this.props.groupBy.length < 2) { - this.props.onChange([...groupBy, { field }]); + handleRemove(field); + } else if (groupBy.length < 2) { + onChange([...groupBy, { field }]); } - this.handleClose(); + closePopover(); }; -} -const StyledContextMenu = euiStyled(EuiContextMenu)` - width: 320px; - & .euiContextMenuItem__text { - overflow: hidden; - text-overflow: ellipsis; - } -`; + const isMaxGroupingsSelected = groupBy.length >= 2; + + const panels: EuiContextMenuPanelDescriptor[] = [ + { + id: 'firstPanel', + title: i18n.translate('xpack.infra.waffle.selectTwoGroupingsTitle', { + defaultMessage: 'Select up to two groupings', + }), + items: [ + { + name: i18n.translate('xpack.infra.waffle.customGroupByOptionName', { + defaultMessage: 'Custom field', + }), + disabled: isMaxGroupingsSelected, + toolTipContent: isMaxGroupingsSelected ? maxGroupByTooltip : null, + icon: 'empty', + panel: 'customPanel', + }, + ...combinedOptions.map((o) => { + const icon = groupBy.some((g) => g.field === o.field) ? 'check' : 'empty'; + const panel = { + name: o.text, + onClick: handleClick(o.field), + icon, + } as EuiContextMenuPanelItemDescriptor; + if (o.toolTipContent) { + panel.toolTipContent = o.toolTipContent; + } + if (isMaxGroupingsSelected && icon === 'empty') { + panel.toolTipContent = maxGroupByTooltip; + panel.disabled = true; + } + return panel; + }), + ], + }, + { + id: 'customPanel', + title: i18n.translate('xpack.infra.waffle.customGroupByPanelTitle', { + defaultMessage: 'Group By Custom Field', + }), + width: 685, + content: , + }, + ]; + const buttonBody = + groupBy.length > 0 ? ( + groupBy + .map((g) => combinedOptions.find((o) => o.field === g.field)) + .filter((o) => o != null) + // In this map the `o && o.field` is totally unnecessary but Typescript is + // too stupid to realize that the filter above prevents the next map from being null + .map((o) => ( + + {o && o.text} + + )) + ) : ( + + ); + + const button = ( + + {buttonBody} + + ); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts index cc1108cb91e6d..87aaad06abc96 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts @@ -6,8 +6,10 @@ */ import { renderHook, act } from '@testing-library/react-hooks'; - +import { DataView } from '@kbn/data-views-plugin/common'; import { useWaffleFilters, WaffleFiltersState } from './use_waffle_filters'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { ResolvedDataView } from '../../../../utils/data_view'; // Mock useUrlState hook jest.mock('react-router-dom', () => ({ @@ -17,9 +19,25 @@ jest.mock('react-router-dom', () => ({ }), })); +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + jest.mock('../../../../containers/metrics_source', () => ({ - useSourceContext: () => ({ - createDerivedIndexPattern: () => 'jestbeat-*', + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'jestbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, }), })); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts index 86f81333e338e..658fb92c468e0 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.ts @@ -18,7 +18,7 @@ import { } from '../../../../../common/inventory_views'; import { useAlertPrefillContext } from '../../../../alerting/use_alert_prefill'; import { useUrlState } from '../../../../utils/use_url_state'; -import { useSourceContext } from '../../../../containers/metrics_source'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; const validateKuery = (expression: string) => { @@ -36,8 +36,7 @@ export const DEFAULT_WAFFLE_FILTERS_STATE: InventoryFiltersState = { }; export const useWaffleFilters = () => { - const { createDerivedIndexPattern } = useSourceContext(); - const indexPattern = createDerivedIndexPattern(); + const { metricsView } = useMetricsDataViewContext(); const [urlState, setUrlState] = useUrlState({ defaultState: DEFAULT_WAFFLE_FILTERS_STATE, @@ -53,8 +52,8 @@ export const useWaffleFilters = () => { const [filterQueryDraft, setFilterQueryDraft] = useState(urlState.expression); const filterQueryAsJson = useMemo( - () => convertKueryToElasticSearchQuery(urlState.expression, indexPattern), - [indexPattern, urlState.expression] + () => convertKueryToElasticSearchQuery(urlState.expression, metricsView?.dataViewReference), + [metricsView?.dataViewReference, urlState.expression] ); const applyFilterQueryFromKueryExpression = useCallback( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx index 3142a82d5e9d2..818a71216c272 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/index.tsx @@ -11,9 +11,6 @@ import { useTrackPageview } from '@kbn/observability-shared-plugin/public'; import { APP_WRAPPER_CLASS } from '@kbn/core/public'; import { css } from '@emotion/react'; import { FilterBar } from './components/filter_bar'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { LayoutView } from './components/layout_view'; import { MetricsPageTemplate } from '../page_template'; @@ -22,14 +19,11 @@ import { SavedViews } from './components/saved_views'; import { SnapshotContainer } from './components/snapshot_container'; import { fullHeightContentStyles } from '../../../page_template.styles'; import { SurveySection } from './components/survey_section'; -import { NoRemoteCluster } from '../../../components/empty_states'; import { WaffleOptionsProvider } from './hooks/use_waffle_options'; import { WaffleTimeProvider } from './hooks/use_waffle_time'; import { WaffleFiltersProvider } from './hooks/use_waffle_filters'; export const SnapshotPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); - useTrackPageview({ app: 'infra_metrics', path: 'inventory' }); useTrackPageview({ app: 'infra_metrics', path: 'inventory', delay: 15000 }); @@ -39,23 +33,6 @@ export const SnapshotPage = () => { }, ]); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading && !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return ( @@ -63,7 +40,6 @@ export const SnapshotPage = () => {
, ], diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx index 31cbe1baae31b..74bf51277c446 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/asset_detail_page.tsx @@ -8,37 +8,14 @@ import React from 'react'; import { useRouteMatch } from 'react-router-dom'; import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; -import { NoRemoteCluster } from '../../../components/empty_states'; -import { SourceErrorPage } from '../../../components/source_error_page'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; -import { useSourceContext } from '../../../containers/metrics_source'; import { AssetDetails } from '../../../components/asset_details'; -import { MetricsPageTemplate } from '../page_template'; import { getAssetDetailsTabs } from '../../../common/asset_details_config/asset_details_tabs'; export const AssetDetailPage = () => { - const { isLoading, loadSourceFailureMessage, loadSource, source } = useSourceContext(); const { params: { type: nodeType, node: nodeId }, } = useRouteMatch<{ type: InventoryItemType; node: string }>(); - const { metricIndicesExist, remoteClustersExist } = source?.status ?? {}; - - if (isLoading || !source) return ; - - if (!remoteClustersExist) { - return ; - } - - if (!metricIndicesExist) { - return ( - - ); - } - - if (loadSourceFailureMessage) - return ; - return ( { renderMode={{ mode: 'page', }} - metricAlias={source.configuration.metricAlias} /> ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx index fdc274a6d06f7..940ff1f92f01d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metric_detail/components/node_details_page.tsx @@ -11,7 +11,6 @@ import moment from 'moment'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { InventoryMetric, InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; import { useTemplateHeaderBreadcrumbs } from '../../../../components/asset_details/hooks/use_page_header'; -import { useSourceContext } from '../../../../containers/metrics_source'; import { useNodeDetails } from '../hooks/use_node_details'; import { MetricsSideNav } from './side_nav'; import { MetricsTimeControls } from './time_controls'; @@ -54,7 +53,6 @@ const parseRange = (range: MetricsTimeInput) => { }; export const NodeDetailsPage = (props: Props) => { - const { metricIndicesExist } = useSourceContext(); const { breadcrumbs } = useTemplateHeaderBreadcrumbs(); const [parsedTimeRange, setParsedTimeRange] = useState(parseRange(props.timeRange)); const { metrics, loading, makeRequest, error } = useNodeDetails( @@ -84,7 +82,6 @@ export const NodeDetailsPage = (props: Props) => { return ( { params: { type: nodeType, node: nodeId }, } = useRouteMatch<{ type: InventoryItemType; node: string }>(); const inventoryModel = findInventoryModel(nodeType); - const { sourceId, metricIndicesExist } = useSourceContext(); + const { sourceId } = useSourceContext(); const parentBreadcrumbResolver = useParentBreadcrumbResolver(); const { @@ -79,7 +79,7 @@ export const MetricDetailPage = () => { if (metadataLoading && !filteredRequiredMetrics.length) { return ( - + void; } export const MetricsExplorerChart = ({ - source, options, chartOptions, series, @@ -119,7 +116,6 @@ export const MetricsExplorerChart = ({ chartOptions={chartOptions} series={series} onFilter={onFilter} - source={source} uiCapabilities={uiCapabilities} /> @@ -132,7 +128,6 @@ export const MetricsExplorerChart = ({ options={options} chartOptions={chartOptions} series={series} - source={source} timeRange={timeRange} uiCapabilities={uiCapabilities} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx index 5a3ed5e2b3268..6ff53e99fd7d2 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.test.tsx @@ -8,17 +8,26 @@ import React from 'react'; import { MetricsExplorerChartContextMenu, Props } from './chart_context_menu'; import { ReactWrapper, mount } from 'enzyme'; -import { - options, - source, - timeRange, - chartOptions, -} from '../../../../utils/fixtures/metrics_explorer'; +import { options, timeRange, chartOptions } from '../../../../utils/fixtures/metrics_explorer'; import { Capabilities } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { coreMock } from '@kbn/core/public/mocks'; +import { MetricsDataViewProvider, SourceProvider } from '../../../../containers/metrics_source'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { DataView } from '@kbn/data-views-plugin/common'; +import { ResolvedDataView } from '../../../../utils/data_view'; const coreStartMock = coreMock.createStart(); + +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + const series = { id: 'exmaple-01', rows: [], columns: [] }; const uiCapabilities: Capabilities = { navLinks: { show: false }, @@ -28,6 +37,24 @@ const uiCapabilities: Capabilities = { infrastructure: { save: true }, }; +jest.mock('../../../../containers/metrics_source', () => ({ + SourceProvider: jest.fn(({ children }) => <>{children}), + MetricsDataViewProvider: jest.fn(({ children }) => <>{children}), + useSourceContext: () => ({ + source: { id: 'default' }, + }), + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const getTestSubject = (component: ReactWrapper, name: string) => { return component.find(`[data-test-subj="${name}"]`).hostNodes(); }; @@ -35,7 +62,11 @@ const getTestSubject = (component: ReactWrapper, name: string) => { const mountComponentWithProviders = (props: Props): ReactWrapper => { return mount( - + + + + + ); }; @@ -55,7 +86,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options, onFilter, @@ -73,7 +103,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, @@ -87,7 +116,6 @@ describe('MetricsExplorerChartContextMenu', () => { it('should not display "Add Filter" without onFilter', async () => { const component = mountComponentWithProviders({ timeRange, - source, series, options, uiCapabilities, @@ -102,7 +130,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, @@ -117,7 +144,6 @@ describe('MetricsExplorerChartContextMenu', () => { const customOptions = { ...options, metrics: [] }; const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, uiCapabilities, @@ -134,7 +160,6 @@ describe('MetricsExplorerChartContextMenu', () => { const onFilter = jest.fn().mockImplementation((query: string) => void 0); const component = mountComponentWithProviders({ timeRange, - source, series, options, onFilter, @@ -152,7 +177,6 @@ describe('MetricsExplorerChartContextMenu', () => { const customOptions = { ...options, groupBy: void 0 }; const component = mountComponentWithProviders({ timeRange, - source, series, options: customOptions, onFilter, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index 0243ccacf136a..2821428d9fa4b 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -18,7 +18,7 @@ import DateMath from '@kbn/datemath'; import { Capabilities } from '@kbn/core/public'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { AlertFlyout } from '../../../../alerting/metric_threshold/components/alert_flyout'; import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; import { @@ -26,7 +26,7 @@ import { MetricsExplorerTimeOptions, MetricsExplorerChartOptions, } from '../hooks/use_metrics_explorer_options'; -import { createTSVBLink } from './helpers/create_tsvb_link'; +import { createTSVBLink, TSVB_WORKAROUND_INDEX_PATTERN } from './helpers/create_tsvb_link'; import { useNodeDetailsRedirect } from '../../../link_to'; import { HOST_FIELD, POD_FIELD, CONTAINER_FIELD } from '../../../../../common/constants'; @@ -34,16 +34,12 @@ export interface Props { options: MetricsExplorerOptions; onFilter?: (query: string) => void; series: MetricsExplorerSeries; - source?: MetricsSourceConfigurationProperties; timeRange: MetricsExplorerTimeOptions; uiCapabilities?: Capabilities; chartOptions: MetricsExplorerChartOptions; } -const fieldToNodeType = ( - source: MetricsSourceConfigurationProperties, - groupBy: string | string[] -): InventoryItemType | undefined => { +const fieldToNodeType = (groupBy: string | string[]): InventoryItemType | undefined => { const fields = Array.isArray(groupBy) ? groupBy : [groupBy]; if (fields.includes(HOST_FIELD)) { return 'host'; @@ -66,7 +62,6 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ onFilter, options, series, - source, timeRange, uiCapabilities, chartOptions, @@ -74,6 +69,7 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ const { getNodeDetailUrl } = useNodeDetailsRedirect(); const [isPopoverOpen, setPopoverState] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); + const { metricsView } = useMetricsDataViewContext(); const supportFiltering = options.groupBy != null && onFilter != null; const handleFilter = useCallback(() => { // onFilter needs check for Typescript even though it's @@ -104,7 +100,7 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ ] : []; - const nodeType = source && options.groupBy && fieldToNodeType(source, options.groupBy); + const nodeType = options.groupBy && fieldToNodeType(options.groupBy); const nodeDetailLinkProps = nodeType ? getNodeDetailUrl({ @@ -117,7 +113,13 @@ export const MetricsExplorerChartContextMenu: React.FC = ({ }) : {}; const tsvbLinkProps = useLinkProps({ - ...createTSVBLink(source, options, series, timeRange, chartOptions), + ...createTSVBLink( + metricsView?.indices ?? TSVB_WORKAROUND_INDEX_PATTERN, + options, + series, + timeRange, + chartOptions + ), }); const viewNodeDetail = nodeType ? [ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx index 6a6728af98629..12ddb26164174 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/charts.tsx @@ -10,7 +10,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { first, last, sumBy } from 'lodash'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { MetricsExplorerResponse } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, @@ -32,7 +31,6 @@ interface Props { data?: { pages: MetricsExplorerResponse[]; }; - source: MetricsSourceConfigurationProperties | undefined; timeRange: MetricsExplorerTimeOptions; } export const MetricsExplorerCharts = ({ @@ -42,16 +40,14 @@ export const MetricsExplorerCharts = ({ options, chartOptions, onRefetch, - onFilter, - source, timeRange, onTimeChange, }: Props) => { if (isLoading) { return ( 1 ? 200 : 400} series={series} - source={source} timeRange={timeRange} onTimeChange={onTimeChange} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx index ce7c9926d2b40..5cc1ad8ec467c 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/group_by.tsx @@ -7,19 +7,18 @@ import { EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; - import React, { useCallback } from 'react'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { MetricsExplorerOptions } from '../hooks/use_metrics_explorer_options'; -import { DerivedIndexPattern } from '../../../../containers/metrics_source'; interface Props { options: MetricsExplorerOptions; onChange: (groupBy: string | null | string[]) => void; - fields: DerivedIndexPattern['fields']; errorOptions?: string[]; } -export const MetricsExplorerGroupBy = ({ options, onChange, fields, errorOptions }: Props) => { +export const MetricsExplorerGroupBy = ({ options, onChange, errorOptions }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const handleChange = useCallback( (selectedOptions: Array<{ label: string }>) => { const groupBy = selectedOptions.map((option) => option.label); @@ -42,6 +41,10 @@ export const MetricsExplorerGroupBy = ({ options, onChange, fields, errorOptions ] : []; + const comboOptions = (metricsView?.fields ?? []) + .filter((f) => f.aggregatable && f.type === 'string') + .map((f) => ({ label: f.name })); + return ( f.aggregatable && f.type === 'string') - .map((f) => ({ label: f.name }))} + options={comboOptions} onChange={handleChange} isClearable={true} /> diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts index ec41d0ff770d1..4c29f8921947d 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.test.ts @@ -6,26 +6,23 @@ */ import { createTSVBLink, createFilterFromOptions } from './create_tsvb_link'; -import { - source, - options, - timeRange, - chartOptions, -} from '../../../../../utils/fixtures/metrics_explorer'; +import { options, timeRange, chartOptions } from '../../../../../utils/fixtures/metrics_explorer'; import { MetricsExplorerYAxisMode, MetricsExplorerChartType, } from '../../hooks/use_metrics_explorer_options'; import { MetricsExplorerOptions } from '../../hooks/use_metrics_explorer_options'; + jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('test-id'), })); +const indexPattern = 'metricbeat-*'; const series = { id: 'example-01', rows: [], columns: [] }; describe('createTSVBLink()', () => { it('should just work', () => { - const link = createTSVBLink(source, options, series, timeRange, chartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -42,7 +39,7 @@ describe('createTSVBLink()', () => { ...options, metrics: [{ aggregation: 'rate', field: 'host.network.egress.bytes' }], }; - const link = createTSVBLink(source, customOptions, series, timeRange, chartOptions); + const link = createTSVBLink(indexPattern, customOptions, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -55,7 +52,7 @@ describe('createTSVBLink()', () => { }); it('should work with time range', () => { const customTimeRange = { ...timeRange, from: 'now-10m', to: 'now' }; - const link = createTSVBLink(source, options, series, customTimeRange, chartOptions); + const link = createTSVBLink(indexPattern, options, series, customTimeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -66,31 +63,10 @@ describe('createTSVBLink()', () => { }, }); }); - it('should work with source', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*', - fields: { ...source.fields, timestamp: 'time' }, - }; - const link = createTSVBLink(customSource, options, series, timeRange, chartOptions); - expect(link).toStrictEqual({ - app: 'visualize', - hash: '/create', - search: { - _a: "(filters:!(),linked:!f,query:(language:kuery,query:''),uiState:(),vis:(aggs:!(),params:(axis_formatter:number,axis_min:0,axis_position:left,axis_scale:normal,default_index_pattern:'my-beats-*',filter:(language:kuery,query:'host.name : \"example-01\"'),id:test-id,index_pattern:'my-beats-*',interval:auto,series:!((axis_position:right,chart_type:line,color:#6092C0,fill:0,formatter:percent,id:test-id,label:'avg(system.cpu.user.pct)',line_width:2,metrics:!((field:system.cpu.user.pct,id:test-id,type:avg)),point_size:0,separate_axis:0,split_mode:everything,stacked:none,value_template:{{value}})),show_grid:1,show_legend:1,time_field:'@timestamp',type:timeseries),title:example-01,type:metrics))", - _g: '(refreshInterval:(pause:!t,value:0),time:(from:now-1h,to:now))', - type: 'metrics', - }, - }); - }); + it('should work with filterQuery', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*', - fields: { ...source.fields, timestamp: 'time' }, - }; const customOptions = { ...options, filterQuery: 'system.network.name:lo*' }; - const link = createTSVBLink(customSource, customOptions, series, timeRange, chartOptions); + const link = createTSVBLink('my-beats-*', customOptions, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -104,7 +80,7 @@ describe('createTSVBLink()', () => { it('should remove axis_min from link', () => { const customChartOptions = { ...chartOptions, yAxisMode: MetricsExplorerYAxisMode.auto }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -118,7 +94,7 @@ describe('createTSVBLink()', () => { it('should change series to area', () => { const customChartOptions = { ...chartOptions, type: MetricsExplorerChartType.area }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -136,7 +112,7 @@ describe('createTSVBLink()', () => { type: MetricsExplorerChartType.area, stack: true, }; - const link = createTSVBLink(source, options, series, timeRange, customChartOptions); + const link = createTSVBLink(indexPattern, options, series, timeRange, customChartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', @@ -149,12 +125,7 @@ describe('createTSVBLink()', () => { }); it('should use the workaround index pattern when there are multiple listed in the source', () => { - const customSource = { - ...source, - metricAlias: 'my-beats-*,metrics-*', - fields: { ...source.fields, timestamp: 'time' }, - }; - const link = createTSVBLink(customSource, options, series, timeRange, chartOptions); + const link = createTSVBLink('my-beats-*,metrics-*', options, series, timeRange, chartOptions); expect(link).toStrictEqual({ app: 'visualize', hash: '/create', diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts index 2725dc46d5cea..3fe9dfc8c5ba6 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/helpers/create_tsvb_link.ts @@ -10,7 +10,6 @@ import { v4 as uuidv4 } from 'uuid'; import { set } from '@kbn/safer-lodash-set'; import { LinkDescriptor } from '@kbn/observability-shared-plugin/public'; import { TIMESTAMP_FIELD } from '../../../../../../common/constants'; -import { MetricsSourceConfigurationProperties } from '../../../../../../common/metrics_sources'; import { colorTransformer, Color } from '../../../../../../common/color_palette'; import { MetricsExplorerSeries } from '../../../../../../common/http_api/metrics_explorer'; import { @@ -31,7 +30,7 @@ import { createMetricLabel } from './create_metric_label'; the field dropdowns are not populated correctly. This index pattern is a temporary fix. See: https://github.com/elastic/kibana/issues/73987 */ -const TSVB_WORKAROUND_INDEX_PATTERN = 'metric*'; +export const TSVB_WORKAROUND_INDEX_PATTERN = 'metric*'; export const metricsExplorerMetricToTSVBMetric = (metric: MetricsExplorerOptionsMetric) => { if (metric.aggregation === 'rate') { @@ -143,15 +142,13 @@ const createTSVBIndexPattern = (alias: string) => { }; export const createTSVBLink = ( - source: MetricsSourceConfigurationProperties | undefined, + indexPattern: string, options: MetricsExplorerOptions, series: MetricsExplorerSeries, timeRange: MetricsExplorerTimeOptions, chartOptions: MetricsExplorerChartOptions ): LinkDescriptor => { - const tsvbIndexPattern = createTSVBIndexPattern( - (source && source.metricAlias) || TSVB_WORKAROUND_INDEX_PATTERN - ); + const tsvbIndexPattern = createTSVBIndexPattern(indexPattern); const appState = { filters: [], linked: false, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index ef2ca1e842ed4..b6918ab140df1 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -8,8 +8,8 @@ import { i18n } from '@kbn/i18n'; import { fromKueryExpression } from '@kbn/es-query'; import React, { useEffect, useState } from 'react'; -import { DataViewBase } from '@kbn/es-query'; import { QuerySuggestion } from '@kbn/unified-search-plugin/public'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { WithKueryAutocompletion } from '../../../../containers/with_kuery_autocompletion'; import { AutocompleteField } from '../../../../components/autocomplete_field'; @@ -22,7 +22,6 @@ type LoadSuggestionsFn = ( export type CurryLoadSuggestionsType = (loadSuggestions: LoadSuggestionsFn) => LoadSuggestionsFn; interface Props { - derivedIndexPattern: DataViewBase; onSubmit: (query: string) => void; onChange?: (query: string) => void; value?: string | null; @@ -41,7 +40,6 @@ function validateQuery(query: string) { } export const MetricsExplorerKueryBar = ({ - derivedIndexPattern, onSubmit, onChange, value, @@ -49,6 +47,7 @@ export const MetricsExplorerKueryBar = ({ curryLoadSuggestions = defaultCurryLoadSuggestions, compressed, }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const [draftQuery, setDraftQuery] = useState(value || ''); const [isValid, setValidation] = useState(true); @@ -67,11 +66,6 @@ export const MetricsExplorerKueryBar = ({ } }; - const filteredDerivedIndexPattern = { - ...derivedIndexPattern, - fields: derivedIndexPattern.fields, - }; - const defaultPlaceholder = i18n.translate( 'xpack.infra.homePage.toolbar.kqlSearchFieldPlaceholder', { @@ -80,7 +74,7 @@ export const MetricsExplorerKueryBar = ({ ); return ( - + {({ isLoadingSuggestions, loadSuggestions, suggestions }) => ( void; - fields: DerivedIndexPattern['fields']; } interface SelectedOption { @@ -26,7 +24,8 @@ interface SelectedOption { label: string; } -export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = false }: Props) => { +export const MetricsExplorerMetrics = ({ options, onChange, autoFocus = false }: Props) => { + const { metricsView } = useMetricsDataViewContext(); const colors = Object.keys(Color) as Array; const [shouldFocus, setShouldFocus] = useState(autoFocus); @@ -54,7 +53,10 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = [onChange, options.aggregation, colors] ); - const comboOptions = fields.map((field) => ({ label: field.name, value: field.name })); + const comboOptions = (metricsView?.fields ?? []).map((field) => ({ + label: field.name, + value: field.name, + })); const selectedOptions = options.metrics .filter((m) => m.aggregation !== 'count') .map((metric) => ({ diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx index 6c8b32fc83d8a..dd866e72cbc3e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/components/toolbar.tsx @@ -25,10 +25,8 @@ import { MetricsExplorerAggregationPicker } from './aggregation'; import { MetricsExplorerChartOptions as MetricsExplorerChartOptionsComponent } from './chart_options'; import { useKibanaUiSetting } from '../../../../utils/use_kibana_ui_setting'; import { mapKibanaQuickRangesToDatePickerRanges } from '../../../../utils/map_timepicker_quickranges_to_datepicker_ranges'; -import { DerivedIndexPattern } from '../../../../containers/metrics_source'; interface Props { - derivedIndexPattern: DerivedIndexPattern; timeRange: MetricsExplorerTimeOptions; options: MetricsExplorerOptions; chartOptions: MetricsExplorerChartOptions; @@ -43,7 +41,6 @@ interface Props { export const MetricsExplorerToolbar = ({ timeRange, - derivedIndexPattern, options, onTimeChange, onRefresh, @@ -81,7 +78,6 @@ export const MetricsExplorerToolbar = ({ @@ -94,22 +90,14 @@ export const MetricsExplorerToolbar = ({ /> - + - + ({ useKibanaTimefilterTime: (defaults: { from: string; to: string }) => [() => defaults], @@ -30,8 +25,7 @@ jest.mock('../../../../alerting/use_alert_prefill', () => ({ })); const renderUseMetricsExplorerStateHook = () => - renderHook((props) => useMetricsExplorerState(props.source, props.derivedIndexPattern), { - initialProps: { source, derivedIndexPattern }, + renderHook(() => useMetricsExplorerState(), { wrapper: ({ children }) => ( {children} ), diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts index 5c498708f94f6..0bcb182879d1f 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metric_explorer_state.ts @@ -7,7 +7,6 @@ import DateMath from '@kbn/datemath'; import { useCallback, useEffect } from 'react'; -import { DataViewBase } from '@kbn/es-query'; import type { MetricsExplorerChartOptions, MetricsExplorerOptions, @@ -15,7 +14,6 @@ import type { MetricsExplorerView, MetricsExplorerViewState, } from '../../../../../common/metrics_explorer_views'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; import { MetricsExplorerMetric, MetricsExplorerAggregation, @@ -25,11 +23,7 @@ import { useMetricsExplorerOptionsContainerContext } from './use_metrics_explore export type { MetricsExplorerViewState }; -export const useMetricsExplorerState = ( - source: MetricsSourceConfigurationProperties, - derivedIndexPattern: DataViewBase, - enabled = true -) => { +export const useMetricsExplorerState = ({ enabled }: { enabled: boolean } = { enabled: true }) => { const { defaultViewState, options, @@ -53,13 +47,11 @@ export const useMetricsExplorerState = ( }); }, [setTimestamps, timeRange]); - const { data, error, fetchNextPage, isLoading } = useMetricsExplorerData( + const { data, error, fetchNextPage, isLoading } = useMetricsExplorerData({ options, - source, - derivedIndexPattern, timestamps, - enabled - ); + enabled, + }); useEffect(() => { refreshTimestamps(); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx index 27647088c9a53..202ae51990ad2 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.test.tsx @@ -8,7 +8,7 @@ import React, { FC, PropsWithChildren } from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useMetricsExplorerData } from './use_metrics_explorer_data'; - +import { DataView } from '@kbn/data-views-plugin/common'; import { renderHook } from '@testing-library/react-hooks'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; @@ -23,6 +23,8 @@ import { import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options'; import { DataViewBase } from '@kbn/es-query'; import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { TIMESTAMP_FIELD } from '../../../../../common/constants'; +import { ResolvedDataView } from '../../../../utils/data_view'; const mockedFetch = jest.fn(); @@ -35,6 +37,28 @@ const queryClient = new QueryClient({ }, }); +const mockDataView = { + id: 'mock-id', + title: 'mock-title', + timeFieldName: TIMESTAMP_FIELD, + isPersisted: () => false, + getName: () => 'mock-data-view', + toSpec: () => ({}), +} as jest.Mocked; + +jest.mock('../../../../containers/metrics_source', () => ({ + useMetricsDataViewContext: () => ({ + metricsView: { + indices: 'metricbeat-*', + timeFieldName: mockDataView.timeFieldName, + fields: mockDataView.fields, + dataViewReference: mockDataView, + } as ResolvedDataView, + loading: false, + error: undefined, + }), +})); + const renderUseMetricsExplorerDataHook = () => { const wrapper: FC> = ({ children }) => { const services = { @@ -55,12 +79,10 @@ const renderUseMetricsExplorerDataHook = () => { derivedIndexPattern: DataViewBase; timestamps: MetricsExplorerTimestamp; }) => - useMetricsExplorerData( - props.options, - props.source, - props.derivedIndexPattern, - props.timestamps - ), + useMetricsExplorerData({ + options: props.options, + timestamps: props.timestamps, + }), { initialProps: { options, diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts index 28377c23da936..39c219101aeb2 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data.ts @@ -5,11 +5,9 @@ * 2.0. */ -import { DataViewBase } from '@kbn/es-query'; import { useInfiniteQuery } from '@tanstack/react-query'; - import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { MetricsSourceConfigurationProperties } from '../../../../../common/metrics_sources'; +import { useMetricsDataViewContext } from '../../../../containers/metrics_source'; import { MetricsExplorerResponse, metricsExplorerResponseRT, @@ -18,14 +16,17 @@ import { convertKueryToElasticSearchQuery } from '../../../../utils/kuery'; import { MetricsExplorerOptions, MetricsExplorerTimestamp } from './use_metrics_explorer_options'; import { decodeOrThrow } from '../../../../../common/runtime_types'; -export function useMetricsExplorerData( - options: MetricsExplorerOptions, - source: MetricsSourceConfigurationProperties | undefined, - derivedIndexPattern: DataViewBase, - { fromTimestamp, toTimestamp, interval }: MetricsExplorerTimestamp, - enabled = true -) { +export function useMetricsExplorerData({ + options, + timestamps: { fromTimestamp, toTimestamp, interval }, + enabled = true, +}: { + options: MetricsExplorerOptions; + timestamps: MetricsExplorerTimestamp; + enabled?: boolean; +}) { const { http } = useKibana().services; + const { metricsView } = useMetricsDataViewContext(); const { isLoading, data, error, refetch, fetchNextPage } = useInfiniteQuery< MetricsExplorerResponse, @@ -39,8 +40,8 @@ export function useMetricsExplorerData( if (!http) { throw new Error('HTTP service is unavailable'); } - if (!source) { - throw new Error('Source is unavailable'); + if (!metricsView?.dataViewReference) { + throw new Error('DataView is unavailable'); } const { afterKey } = pageParam; @@ -54,10 +55,13 @@ export function useMetricsExplorerData( groupInstance: options.groupInstance, afterKey, limit: options.limit, - indexPattern: source.metricAlias, + indexPattern: metricsView.indices, filterQuery: (options.filterQuery && - convertKueryToElasticSearchQuery(options.filterQuery, derivedIndexPattern)) || + convertKueryToElasticSearchQuery( + options.filterQuery, + metricsView.dataViewReference + )) || void 0, timerange: { interval, @@ -71,7 +75,7 @@ export function useMetricsExplorerData( return decodeOrThrow(metricsExplorerResponseRT)(response); }, getNextPageParam: (lastPage) => lastPage.pageInfo, - enabled: enabled && !!fromTimestamp && !!toTimestamp && !!http && !!source, + enabled: enabled && !!fromTimestamp && !!toTimestamp && !!http && !!metricsView, refetchOnWindowFocus: false, }); diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx index b1cea81cec052..887b6be224a45 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/metrics_explorer/index.tsx @@ -5,33 +5,43 @@ * 2.0. */ -import { EuiErrorBoundary } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { useTrackPageview, FeatureFeedbackButton } from '@kbn/observability-shared-plugin/public'; +import { WithMetricsExplorerOptionsUrlState } from '../../../containers/metrics_explorer/with_metrics_explorer_options_url_state'; import { useKibanaEnvironmentContext } from '../../../hooks/use_kibana'; -import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useMetricsExplorerViews } from '../../../hooks/use_metrics_explorer_views'; -import { MetricsSourceConfigurationProperties } from '../../../../common/metrics_sources'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; -import { NoData, NoRemoteCluster } from '../../../components/empty_states'; +import { NoData } from '../../../components/empty_states'; import { MetricsExplorerCharts } from './components/charts'; import { MetricsExplorerToolbar } from './components/toolbar'; import { useMetricsExplorerState } from './hooks/use_metric_explorer_state'; -import { useSourceContext } from '../../../containers/metrics_source'; import { MetricsPageTemplate } from '../page_template'; import { metricsExplorerTitle } from '../../../translations'; -import { DerivedIndexPattern } from '../../../containers/metrics_source'; import { SavedViews } from './components/saved_views'; - -interface MetricsExplorerPageProps { - source: MetricsSourceConfigurationProperties; - derivedIndexPattern: DerivedIndexPattern; -} +import { MetricsExplorerOptionsContainer } from './hooks/use_metrics_explorer_options'; const METRICS_EXPLORER_FEEDBACK_URL = 'https://ela.st/survey-infra-metricsexplorer?usp=pp_url'; -export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExplorerPageProps) => { +export const MetricsExplorerPage = () => { + useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); + useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); + + useMetricsBreadcrumbs([ + { + text: metricsExplorerTitle, + }, + ]); + + return ( + + + + + ); +}; + +const MetricsExplorerContent = () => { const [enabled, setEnabled] = useState(false); const { isLoading, @@ -49,16 +59,14 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl handleLoadMore, onViewStateChange, refresh, - } = useMetricsExplorerState(source, derivedIndexPattern, enabled); + } = useMetricsExplorerState({ enabled }); const { currentView } = useMetricsExplorerViews(); - const { source: sourceContext, metricIndicesExist } = useSourceContext(); + const { kibanaVersion, isCloudEnv, isServerlessEnv } = useKibanaEnvironmentContext(); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer' }); useTrackPageview({ app: 'infra_metrics', path: 'metrics_explorer', delay: 15000 }); - const { remoteClustersExist } = sourceContext?.status ?? {}; - useEffect(() => { if (currentView) { onViewStateChange(currentView); @@ -84,68 +92,57 @@ export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExpl currentTimerange: timeRange, }; - if (isLoading && !sourceContext) return ; - - if (!remoteClustersExist) { - return ; - } - return ( - - , - , - ], - }} - > - , + , + ], + }} + > + + {error ? ( + + ) : ( + - {error ? ( - - ) : ( - - )} - - + )} + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx index 35baef037c4e2..74128ad8eb41e 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/page_template.tsx @@ -12,16 +12,15 @@ import React, { useEffect } from 'react'; import { noMetricIndicesPromptDescription, noMetricIndicesPromptPrimaryActionTitle, + NoRemoteCluster, } from '../../components/empty_states'; -import { useSourceContext } from '../../containers/metrics_source'; +import { SourceErrorPage } from '../../components/source_error_page'; +import { SourceLoadingPage } from '../../components/source_loading_page'; +import { useMetricsDataViewContext, useSourceContext } from '../../containers/metrics_source'; import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; +import { ErrorCallout } from './hosts/components/error_callout'; -interface MetricsPageTemplateProps extends LazyObservabilityPageTemplateProps { - hasData?: boolean; -} - -export const MetricsPageTemplate: React.FC = ({ - hasData = true, +export const MetricsPageTemplate: React.FC = ({ 'data-test-subj': _dataTestSubj, ...pageTemplateProps }) => { @@ -35,9 +34,11 @@ export const MetricsPageTemplate: React.FC = ({ }, } = useKibanaContextForPlugin(); - const { source } = useSourceContext(); + const { source, error: sourceError, loadSource, isLoading } = useSourceContext(); + const { error: dataViewLoadError, refetch: loadDataView } = useMetricsDataViewContext(); + const { remoteClustersExist, metricIndicesExist } = source?.status ?? {}; - const noDataConfig: NoDataConfig | undefined = hasData + const noDataConfig: NoDataConfig | undefined = metricIndicesExist ? undefined : { solution: i18n.translate('xpack.infra.metrics.noDataConfig.solutionName', { @@ -64,7 +65,7 @@ export const MetricsPageTemplate: React.FC = ({ }, ], starterPrompts: [ - ...(!hasData + ...(!metricIndicesExist ? [ { title: i18n.translate( @@ -85,11 +86,37 @@ export const MetricsPageTemplate: React.FC = ({ : []), ], }); - }, [hasData, setScreenContext, source]); + }, [metricIndicesExist, setScreenContext, source]); + + if (isLoading && !source) return ; + + if (!remoteClustersExist) { + return ; + } + + if (sourceError) { + ; + } + + if (dataViewLoadError) { + ; + } return ( diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx index 9dd18644c30f1..de078dcb354df 100644 --- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -25,6 +25,7 @@ import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, METRIC_THRESHOLD_ALERT_TYPE_ID, } from '@kbn/rule-data-utils'; +import { PageTemplate } from '../../../components/page_template'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; @@ -34,8 +35,6 @@ import { NameConfigurationPanel } from './name_configuration_panel'; import { useSourceConfigurationFormState } from './source_configuration_form_state'; import { useMetricsBreadcrumbs } from '../../../hooks/use_metrics_breadcrumbs'; import { settingsTitle } from '../../../translations'; - -import { MetricsPageTemplate } from '../page_template'; import { FeaturesConfigurationPanel } from './features_configuration_panel'; interface SourceConfigurationSettingsProps { shouldAllowEdit: boolean; @@ -72,12 +71,10 @@ export const SourceConfigurationSettings = ({ }, [http]); const { - createSourceConfiguration, + persistSourceConfiguration: updateSourceConfiguration, source, sourceExists, isLoading, - isUninitialized, - updateSourceConfiguration, } = useSourceContext(); const { @@ -103,9 +100,7 @@ export const SourceConfigurationSettings = ({ const persistUpdates = useCallback(async () => { await Promise.all([ - sourceExists - ? updateSourceConfiguration(formStateChanges) - : createSourceConfiguration(formState), + updateSourceConfiguration(sourceExists ? formStateChanges : formState), infraUiSettings.saveAll(), ]); resetForm(); @@ -115,7 +110,6 @@ export const SourceConfigurationSettings = ({ updateSourceConfiguration, formStateChanges, infraUiSettings, - createSourceConfiguration, formState, ]); @@ -132,12 +126,12 @@ export const SourceConfigurationSettings = ({ const { hasInfraMLCapabilities } = useInfraMLCapabilitiesContext(); - if ((isLoading || isUninitialized) && !source) { + if (isLoading && !source) { return ; } return ( - )} - + ); }; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts b/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts new file mode 100644 index 0000000000000..73d3e8c930ecb --- /dev/null +++ b/x-pack/plugins/observability_solution/infra/public/utils/data_view.ts @@ -0,0 +1,85 @@ +/* + * 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 { DataView, DataViewsContract, type FieldSpec } from '@kbn/data-views-plugin/common'; +import { TIMESTAMP_FIELD } from '../../common/constants'; + +export interface ResolvedDataView { + dataViewReference: DataView; + indices: string; + fields: FieldSpec[]; + timeFieldName: string; +} + +interface PersistedDataView { + dataViewsService: DataViewsContract; + dataViewId: string; +} + +interface DataViewAttributes { + timeFieldName: string; + name?: string; +} + +export const resolveDataView = ({ + dataViewId, + dataViewsService, +}: { + dataViewId: string; + dataViewsService: DataViewsContract; +}) => { + try { + return resolvePersistedDataView({ dataViewsService, dataViewId }); + } catch { + return resolveAdHocDataView({ + dataViewsService, + dataViewId, + attributes: { + timeFieldName: TIMESTAMP_FIELD, + }, + }); + } +}; + +export const resolvePersistedDataView = async ({ + dataViewsService, + dataViewId, +}: PersistedDataView): Promise => { + const dataView = await dataViewsService.get(dataViewId, false); + + return { + indices: dataView.getIndexPattern(), + timeFieldName: dataView.timeFieldName ?? TIMESTAMP_FIELD, + fields: dataView.fields ?? [], + dataViewReference: dataView, + }; +}; + +export const resolveAdHocDataView = async ({ + dataViewsService, + dataViewId, + attributes, +}: PersistedDataView & { attributes: DataViewAttributes }): Promise => { + const { name, timeFieldName } = attributes; + const dataViewReference = await dataViewsService.create( + { + id: dataViewId, + name, + title: dataViewId, + timeFieldName, + }, + false, + false + ); + + return { + indices: dataViewId, + timeFieldName, + fields: dataViewReference.fields, + dataViewReference, + }; +}; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts index 0aee9c00814c6..9beef06244b63 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/filters/build.ts @@ -36,8 +36,8 @@ export const buildCombinedAssetFilter = ({ meta: {}, }; } - const filtersFromValues = values.map((value) => buildPhraseFilter(indexField, value, dataView)); + const filtersFromValues = values.map((value) => buildPhraseFilter(indexField, value, dataView)); return buildCombinedFilter(BooleanRelation.OR, filtersFromValues, dataView); }; diff --git a/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts b/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts index aec9ec58aabaa..b74549359a9c7 100644 --- a/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts +++ b/x-pack/plugins/observability_solution/infra/public/utils/kuery.ts @@ -10,7 +10,7 @@ import { DataViewBase } from '@kbn/es-query'; export const convertKueryToElasticSearchQuery = ( kueryExpression: string, - indexPattern: DataViewBase, + indexPattern?: DataViewBase, swallowErrors: boolean = true ) => { try { From afb3d37469f8c8e79240918938a06eacdf41140e Mon Sep 17 00:00:00 2001 From: elena-shostak <165678770+elena-shostak@users.noreply.github.com> Date: Mon, 27 May 2024 12:25:44 +0200 Subject: [PATCH 06/59] [Spaces] Space solution property (#183986) ## Summary Added solution property for the space. - Forbidden in serverless. - To facilitate iterative development made the property as optional in stateful offering until all of the workstreams are complete. ### How to test API changes ``` # Should create space POST kbn:/api/spaces/space { "name": "space without solution", "id": "my-space-solution-1", "description": "a description", "color": "#5c5959", "disabledFeatures": [] } # Should fail with 400 POST kbn:/api/spaces/space { "name": "space with solution", "id": "my-space-solution-2", "description": "a description", "color": "#5c5959", "solution": "some_solution", "disabledFeatures": [] } # Should fail with 400 POST kbn:/api/spaces/space { "name": "space with solution", "id": "my-space-solution-2", "description": "a description", "color": "#5c5959", "solution": null, "disabledFeatures": [] } # Should create space POST kbn:/api/spaces/space { "name": "space with solution", "id": "my-space-solution-2", "description": "a description", "color": "#5c5959", "solution": "search", "disabledFeatures": [] } # Should get 'my-space-solution-1' space without solution field GET kbn:/api/spaces/space/my-space-solution-1 # Should get 'my-space-solution-2' space with solution field GET kbn:/api/spaces/space/my-space-solution-2 # Should fail to update with 400 PUT kbn:/api/spaces/space/my-space-solution-1 { "id": "my-space-solution-1", "name": "my-space-solution-1 name", "solution": "some_solution" } # Should fail to update with 400 PUT kbn:/api/spaces/space/my-space-solution-1 { "id": "my-space-solution-1", "name": "my-space-solution-1 name", "solution": null } # Should update 'my-space-solution-1' PUT kbn:/api/spaces/space/my-space-solution-1 { "id": "my-space-solution-1", "name": "my-space-solution-1 name", "solution": "security" } # Should get 'my-space-solution-1' space wit solution field set to 'security' GET kbn:/api/spaces/space/my-space-solution-1 # Should return list where # 1. 'my-space-solution-1' has solution 'security' # 2. 'my-space-solution-2' has solution 'search' # 3. Other spaces don't have solution field present GET kbn:/api/spaces/space ``` ### Checklist - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed ([Security and Spaces config](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6076), [Spaces only config](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6075)) ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Fixes: https://github.com/elastic/kibana/issues/183559__ ## Release note Added optional solution property for Space in a stateful offering. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- docs/api/spaces-management/get.asciidoc | 3 +- docs/api/spaces-management/get_all.asciidoc | 3 +- docs/api/spaces-management/post.asciidoc | 3 + docs/api/spaces-management/put.asciidoc | 3 + .../current_fields.json | 3 +- .../current_mappings.json | 3 + .../check_registered_types.test.ts | 2 +- .../group3/migration_from_older_v1.test.ts | 13 +- .../plugins/spaces/common/types/space/v1.ts | 5 + .../spaces/server/lib/space_schema.test.ts | 77 ++-- .../plugins/spaces/server/lib/space_schema.ts | 17 +- x-pack/plugins/spaces/server/plugin.ts | 23 +- .../routes/api/external/copy_to_space.test.ts | 3 +- .../server/routes/api/external/delete.test.ts | 3 +- .../disable_legacy_url_aliases.test.ts | 3 +- .../server/routes/api/external/get.test.ts | 3 +- .../spaces/server/routes/api/external/get.ts | 4 +- .../routes/api/external/get_all.test.ts | 3 +- .../external/get_shareable_references.test.ts | 3 +- .../server/routes/api/external/index.ts | 7 +- .../server/routes/api/external/post.test.ts | 3 +- .../spaces/server/routes/api/external/post.ts | 6 +- .../server/routes/api/external/put.test.ts | 3 +- .../spaces/server/routes/api/external/put.ts | 6 +- .../external/update_objects_spaces.test.ts | 3 +- .../api/internal/get_content_summary.test.ts | 2 +- .../spaces/server/saved_objects/mappings.ts | 3 + .../saved_objects/saved_objects_service.ts | 25 ++ .../spaces_client/spaces_client.test.ts | 328 +++++++++++++++++- .../server/spaces_client/spaces_client.ts | 37 +- .../spaces_client_service.test.ts | 14 +- .../spaces_client/spaces_client_service.ts | 9 +- .../spaces_service/spaces_service.test.ts | 2 +- .../common/suites/create.ts | 31 ++ .../security_and_spaces/apis/create.ts | 33 ++ .../spaces_only/apis/create.ts | 5 + 36 files changed, 605 insertions(+), 89 deletions(-) diff --git a/docs/api/spaces-management/get.asciidoc b/docs/api/spaces-management/get.asciidoc index 48245b7786604..d2dbeb0a3f4b4 100644 --- a/docs/api/spaces-management/get.asciidoc +++ b/docs/api/spaces-management/get.asciidoc @@ -31,6 +31,7 @@ The API returns the following: "color": "#aabbcc", "initials": "MK", "disabledFeatures": [], - "imageUrl": "" + "imageUrl": "", + "solution": "search" } -------------------------------------------------- diff --git a/docs/api/spaces-management/get_all.asciidoc b/docs/api/spaces-management/get_all.asciidoc index 3c95b1b904441..0fd332c12b739 100644 --- a/docs/api/spaces-management/get_all.asciidoc +++ b/docs/api/spaces-management/get_all.asciidoc @@ -71,7 +71,8 @@ The API returns the following: "name": "Sales", "initials": "MK", "disabledFeatures": ["discover"], - "imageUrl": "" + "imageUrl": "", + "solution": "observability" } ] -------------------------------------------------- diff --git a/docs/api/spaces-management/post.asciidoc b/docs/api/spaces-management/post.asciidoc index 035fe897da251..b72d4df79c3d6 100644 --- a/docs/api/spaces-management/post.asciidoc +++ b/docs/api/spaces-management/post.asciidoc @@ -36,6 +36,9 @@ experimental[] Create a {kib} space. (Optional, string) The data-URL encoded image to display in the space avatar. If specified, `initials` will not be displayed, and the `color` will be visible as the background color for transparent images. For best results, your image should be 64x64. Images will not be optimized by this API call, so care should be taken when using custom images. +`solution`:: + (Optional, string) The solution defined for the space. Can be one of `security`, `observability`, `search`, `classic` + [[spaces-api-post-response-codes]] ==== Response codes diff --git a/docs/api/spaces-management/put.asciidoc b/docs/api/spaces-management/put.asciidoc index e8f75ee089d77..0d1c8d5f2e779 100644 --- a/docs/api/spaces-management/put.asciidoc +++ b/docs/api/spaces-management/put.asciidoc @@ -36,6 +36,9 @@ experimental[] Update an existing {kib} space. (Optional, string) Specifies the data-url encoded image to display in the space avatar. If specified, `initials` will not be displayed, and the `color` will be visible as the background color for transparent images. For best results, your image should be 64x64. Images will not be optimized by this API call, so care should be taken when using custom images. +`solution`:: + (Optional, string) The solution defined for the space. Can be one of `security`, `observability`, `search`, `classic`. + [[spaces-api-put-response-codes]] ==== Response codes diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index ad653171f2707..703730a262281 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -931,7 +931,8 @@ ], "slo-settings": [], "space": [ - "name" + "name", + "solution" ], "spaces-usage-stats": [], "synthetics-monitor": [ diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index 6d54282cabafe..94f2148280f08 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -3062,6 +3062,9 @@ } }, "type": "text" + }, + "solution": { + "type": "keyword" } } }, diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 08c1c039d5e79..1de50f0547b35 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -149,7 +149,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline-pinned-event": "082daa3ce647b33873f6abccf340bdfa32057c8d", "slo": "9a9995e4572de1839651c43b5fc4dc8276bb5815", "slo-settings": "f6b5ed339470a6a2cda272bde1750adcf504a11b", - "space": "8de4ec513e9bbc6b2f1d635161d850be7747d38e", + "space": "d38fa4bc669b9b1d6ec86aac2983d4c6675723ed", "spaces-usage-stats": "3abca98713c52af8b30300e386c7779b3025a20e", "synthetics-monitor": "5ceb25b6249bd26902c9b34273c71c3dce06dbea", "synthetics-param": "3ebb744e5571de678b1312d5c418c8188002cf5e", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts index 8a4b584f34d84..186a90e47da71 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/migration_from_older_v1.test.ts @@ -15,6 +15,7 @@ import { Env } from '@kbn/config'; import { getEnvOptions } from '@kbn/config-mocks'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import type { SavedObjectsRawDoc } from '@kbn/core-saved-objects-server'; +import { modelVersionToVirtualVersion } from '@kbn/core-saved-objects-base-server-internal'; import { createTestServers, createRootWithCorePlugins, @@ -125,7 +126,7 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { .getTypeRegistry() .getAllTypes() .reduce((versionMap, type) => { - const { name, migrations, convertToMultiNamespaceTypeVersion } = type; + const { name, migrations, convertToMultiNamespaceTypeVersion, modelVersions } = type; if (migrations || convertToMultiNamespaceTypeVersion) { const migrationsMap = typeof migrations === 'function' ? migrations() : migrations; const migrationsKeys = migrationsMap ? Object.keys(migrationsMap) : []; @@ -133,6 +134,16 @@ describe('migrating from 7.3.0-xpack which used v1 migrations', () => { // Setting this option registers a conversion migration that is reflected in the object's `typeMigrationVersions` field migrationsKeys.push(convertToMultiNamespaceTypeVersion); } + + const modelVersionCreateSchemas = + typeof modelVersions === 'function' ? modelVersions() : modelVersions ?? {}; + + Object.entries(modelVersionCreateSchemas).forEach(([key, modelVersion]) => { + if (modelVersion.schemas?.create) { + migrationsKeys.push(modelVersionToVirtualVersion(key)); + } + }); + const highestVersion = migrationsKeys.sort(Semver.compare).reverse()[0]; return { ...versionMap, diff --git a/x-pack/plugins/spaces/common/types/space/v1.ts b/x-pack/plugins/spaces/common/types/space/v1.ts index 0d0a32d758da8..e18ad5e656efc 100644 --- a/x-pack/plugins/spaces/common/types/space/v1.ts +++ b/x-pack/plugins/spaces/common/types/space/v1.ts @@ -58,6 +58,11 @@ export interface Space { * @private */ _reserved?: boolean; + + /** + * Solution selected for this space. + */ + solution?: 'security' | 'observability' | 'search' | 'classic'; } /** diff --git a/x-pack/plugins/spaces/server/lib/space_schema.test.ts b/x-pack/plugins/spaces/server/lib/space_schema.test.ts index d6911a987786e..c31d7f5ca3c22 100644 --- a/x-pack/plugins/spaces/server/lib/space_schema.test.ts +++ b/x-pack/plugins/spaces/server/lib/space_schema.test.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { spaceSchema } from './space_schema'; +import { getSpaceSchema } from './space_schema'; + +// non-serverless space schema +const spaceBaseSchema = getSpaceSchema(false); +const spaceServerlessSchema = getSpaceSchema(true); const defaultProperties = { id: 'foo', @@ -15,7 +19,7 @@ const defaultProperties = { describe('#id', () => { test('is required', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: undefined, }) @@ -26,7 +30,7 @@ describe('#id', () => { test('allows lowercase a-z, 0-9, "_" and "-"', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: 'abcdefghijklmnopqrstuvwxyz0123456789_-', }) @@ -35,7 +39,7 @@ describe('#id', () => { test(`doesn't allow uppercase`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: 'Foo', }) @@ -46,7 +50,7 @@ describe('#id', () => { test(`doesn't allow an empty string`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: '', }) @@ -59,7 +63,7 @@ describe('#id', () => { (invalidCharacter) => { test(`doesn't allow ${invalidCharacter}`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, id: `foo-${invalidCharacter}`, }) @@ -72,7 +76,7 @@ describe('#id', () => { describe('#disabledFeatures', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: undefined, }) @@ -80,7 +84,7 @@ describe('#disabledFeatures', () => { }); test('defaults to an empty array', () => { - const result = spaceSchema.validate({ + const result = spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: undefined, }); @@ -89,7 +93,7 @@ describe('#disabledFeatures', () => { test('must be an array if provided', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: 'foo', }) @@ -100,7 +104,7 @@ describe('#disabledFeatures', () => { test('allows an array of strings', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: ['foo', 'bar'], }) @@ -109,7 +113,7 @@ describe('#disabledFeatures', () => { test('does not allow an array containing non-string elements', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, disabledFeatures: ['foo', true], }) @@ -122,7 +126,7 @@ describe('#disabledFeatures', () => { describe('#color', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: undefined, }) @@ -131,7 +135,7 @@ describe('#color', () => { test(`doesn't allow an empty string`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '', }) @@ -142,7 +146,7 @@ describe('#color', () => { test(`allows lower case hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#aabbcc', }) @@ -151,7 +155,7 @@ describe('#color', () => { test(`allows upper case hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#AABBCC', }) @@ -160,7 +164,7 @@ describe('#color', () => { test(`allows numeric hex color code`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '#123456', }) @@ -169,7 +173,7 @@ describe('#color', () => { test(`must start with a hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '123456', }) @@ -180,7 +184,7 @@ describe('#color', () => { test(`cannot exceed 6 digits following the hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '1234567', }) @@ -191,7 +195,7 @@ describe('#color', () => { test(`cannot be fewer than 6 digits following the hash`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, color: '12345', }) @@ -204,7 +208,7 @@ describe('#color', () => { describe('#imageUrl', () => { test('is optional', () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: undefined, }) @@ -213,7 +217,7 @@ describe('#imageUrl', () => { test(`must start with data:image`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: 'notValid', }) @@ -222,7 +226,7 @@ describe('#imageUrl', () => { test(`checking that a valid image is accepted as imageUrl`, () => { expect(() => - spaceSchema.validate({ + spaceBaseSchema.validate({ ...defaultProperties, imageUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAIAAADYYG7QAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTnU1rJkAAAB3klEQVRYR+2WzUrDQBCARzwqehE8ir1WPfgqRRA1bePBXgpe/MGCB9/Aiw+j+ASCB6kotklaEwW1F0WwNSaps9lV69awGzBpDzt8pJP9mXxsmk3ABH2oUEIilJAIJSRCCYlQQiKUkIh4QgY5agZodVjBowFrBktWQzDBU2ykiYaDuQpCYgnl3QunGzM6Z6YF+b5SkcgK1UH/aLbYReQiYL9d9/o+XFop5IU0Vl4uapAzoXC3eEBPw9vH1/wT6Vs2otPSkoH/IZzlzO/TU2vgQm8nl69Hp0H7nZ4OXogLJSSKBIUC3w88n+Ueyfv56fVZnqCQNVnCHbLrkV0Gd2d+GNkglsk438dhaTxloZDutV4wb06Vf40JcWZ2sMttPpE8NaHGeBnzIAhwPXqHseVB11EyLD0hxLUeaYud2a3B0g3k7GyFtrhX7F2RqhC+yV3jgTb2Rqdqf7/kUxYiWBOlTtXxfPJEtc8b5thGb+8AhL4ohnCNqQjZ2T2+K5rnw2M6KwEhKNDSGM3pTdxjhDgLbHkw/v/zw4AiPuSsfMzAiTidKxiF/ArpFqyzK8SMOlkwvloUMYRCtNvZLWeuIomd2Za/WZS4QomjhEQoIRFKSIQSEqGERAyfEH4YDBFQ/ARU6BiBxCAIQQAAAABJRU5ErkJggg==', @@ -230,3 +234,32 @@ describe('#imageUrl', () => { ).not.toThrowError(); }); }); + +describe('#solution', () => { + it('should throw error if solution is defined in serverless offering', () => { + expect(() => + spaceServerlessSchema.validate({ ...defaultProperties, solution: 'search' }) + ).toThrow(); + }); + + it('should not throw error if solution is undefined in classic offering', () => { + expect(() => + spaceBaseSchema.validate({ ...defaultProperties, solution: undefined }, {}) + ).not.toThrow(); + }); + + it('should throw error if solution is invalid in classic offering', () => { + expect(() => spaceBaseSchema.validate({ ...defaultProperties, solution: 'some_value' }, {})) + .toThrowErrorMatchingInlineSnapshot(` + "[solution]: types that failed validation: + - [solution.0]: expected value to equal [security] + - [solution.1]: expected value to equal [observability] + - [solution.2]: expected value to equal [search] + - [solution.3]: expected value to equal [classic]" + `); + + expect(() => + spaceBaseSchema.validate({ ...defaultProperties, solution: ' search ' }, {}) + ).toThrow(); + }); +}); diff --git a/x-pack/plugins/spaces/server/lib/space_schema.ts b/x-pack/plugins/spaces/server/lib/space_schema.ts index b736994c4d13b..cb184e322b5a9 100644 --- a/x-pack/plugins/spaces/server/lib/space_schema.ts +++ b/x-pack/plugins/spaces/server/lib/space_schema.ts @@ -11,7 +11,7 @@ import { MAX_SPACE_INITIALS } from '../../common'; export const SPACE_ID_REGEX = /^[a-z0-9_\-]+$/; -export const spaceSchema = schema.object({ +const spaceSchema = schema.object({ id: schema.string({ validate: (value) => { if (!SPACE_ID_REGEX.test(value)) { @@ -43,3 +43,18 @@ export const spaceSchema = schema.object({ }) ), }); + +const solutionSchema = schema.oneOf([ + schema.literal('security'), + schema.literal('observability'), + schema.literal('search'), + schema.literal('classic'), +]); + +export const getSpaceSchema = (isServerless: boolean) => { + if (isServerless) { + return spaceSchema; + } + + return spaceSchema.extends({ solution: schema.maybe(solutionSchema) }); +}; diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index 9084a74e24265..a20396c3e4695 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -125,7 +125,10 @@ export class SpacesPlugin this.hasOnlyDefaultSpace$ = this.config$.pipe(map(({ maxSpaces }) => maxSpaces === 1)); this.log = initializerContext.logger.get(); this.spacesService = new SpacesService(); - this.spacesClientService = new SpacesClientService((message) => this.log.debug(message)); + this.spacesClientService = new SpacesClientService( + (message) => this.log.debug(message), + initializerContext.env.packageInfo.buildFlavor + ); } public setup(core: CoreSetup, plugins: PluginsSetup): SpacesPluginSetup { @@ -168,16 +171,14 @@ export class SpacesPlugin const router = core.http.createRouter(); - initExternalSpacesApi( - { - router, - log: this.log, - getStartServices: core.getStartServices, - getSpacesService, - usageStatsServicePromise, - }, - this.initializerContext.env.packageInfo.buildFlavor - ); + initExternalSpacesApi({ + router, + log: this.log, + getStartServices: core.getStartServices, + getSpacesService, + usageStatsServicePromise, + isServerless: this.initializerContext.env.packageInfo.buildFlavor === 'serverless', + }); initInternalSpacesApi({ router, diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts index 7722316799076..8c5782aacd519 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts @@ -57,7 +57,7 @@ describe('copy to space', () => { createResolveSavedObjectsImportErrorsMock() ); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -85,6 +85,7 @@ describe('copy to space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[ctsRouteDefinition, ctsRouteHandler], [resolveRouteDefinition, resolveRouteHandler]] = diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts index 09ed757a5df74..99ec34917eb5a 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts @@ -42,7 +42,7 @@ describe('Spaces Public API', () => { const coreStart = coreMock.createStart(); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('Spaces Public API', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.delete.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts b/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts index ed7c403182ab5..e6f665f817c55 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/disable_legacy_url_aliases.test.ts @@ -42,7 +42,7 @@ describe('_disable_legacy_url_aliases', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -70,6 +70,7 @@ describe('_disable_legacy_url_aliases', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts index 34c1ed01e0ce4..938af432b0cfa 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts @@ -41,7 +41,7 @@ describe('GET space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -66,6 +66,7 @@ describe('GET space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.ts b/x-pack/plugins/spaces/server/routes/api/external/get.ts index ce89aac5fe186..496c3408b7d2e 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.ts @@ -30,7 +30,9 @@ export function initGetSpaceApi(deps: ExternalRouteDeps) { try { const space = await spacesClient.get(spaceId); - return response.ok({ body: space }); + return response.ok({ + body: space, + }); } catch (error) { if (SavedObjectsErrorHelpers.isNotFoundError(error)) { return response.notFound(); diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts index aefef389c7d26..a3d4f151c6615 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts @@ -43,7 +43,7 @@ describe('GET /spaces/space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -68,6 +68,7 @@ describe('GET /spaces/space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts index c9ea0d71c11b7..8c9c60f78f461 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_shareable_references.test.ts @@ -42,7 +42,7 @@ describe('get shareable references', () => { const { savedObjects, savedObjectsClient } = createMockSavedObjectsService(spaces); coreStart.savedObjects = savedObjects; - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -66,6 +66,7 @@ describe('get shareable references', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[getShareableReferences, getShareableReferencesRouteHandler]] = router.post.mock.calls; diff --git a/x-pack/plugins/spaces/server/routes/api/external/index.ts b/x-pack/plugins/spaces/server/routes/api/external/index.ts index 290807f9b5f4d..6b919b6898709 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/index.ts @@ -4,8 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import type { BuildFlavor } from '@kbn/config/src/types'; import type { CoreSetup, Logger } from '@kbn/core/server'; import { initCopyToSpacesApi } from './copy_to_space'; @@ -27,9 +25,10 @@ export interface ExternalRouteDeps { getSpacesService: () => SpacesServiceStart; usageStatsServicePromise: Promise; log: Logger; + isServerless: boolean; } -export function initExternalSpacesApi(deps: ExternalRouteDeps, buildFlavor: BuildFlavor) { +export function initExternalSpacesApi(deps: ExternalRouteDeps) { // These two routes are always registered, internal in serverless by default initGetSpaceApi(deps); initGetAllSpacesApi(deps); @@ -37,7 +36,7 @@ export function initExternalSpacesApi(deps: ExternalRouteDeps, buildFlavor: Buil // In the serverless environment, Spaces are enabled but are effectively hidden from the user. We // do not support more than 1 space: the default space. These HTTP APIs for creating, deleting, // updating, and manipulating saved objects across multiple spaces are not needed. - if (buildFlavor !== 'serverless') { + if (!deps.isServerless) { initPutSpacesApi(deps); initDeleteSpacesApi(deps); initPostSpacesApi(deps); diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts index 88c763f31c0be..b36a3619a48e0 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts @@ -42,7 +42,7 @@ describe('Spaces Public API', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('Spaces Public API', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.ts b/x-pack/plugins/spaces/server/routes/api/external/post.ts index d8091a0140e00..34a9c7f854dad 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.ts @@ -11,17 +11,17 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { ExternalRouteDeps } from '.'; import { wrapError } from '../../../lib/errors'; -import { spaceSchema } from '../../../lib/space_schema'; +import { getSpaceSchema } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; export function initPostSpacesApi(deps: ExternalRouteDeps) { - const { router, log, getSpacesService } = deps; + const { router, log, getSpacesService, isServerless } = deps; router.post( { path: '/api/spaces/space', validate: { - body: spaceSchema, + body: getSpaceSchema(isServerless), }, }, createLicensedRouteHandler(async (context, request, response) => { diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts index 5e1e6077a758e..2141e369507b9 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts @@ -42,7 +42,7 @@ describe('PUT /api/spaces/space', () => { const log = loggingSystemMock.create().get('spaces'); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('PUT /api/spaces/space', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [routeDefinition, routeHandler] = router.put.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.ts b/x-pack/plugins/spaces/server/routes/api/external/put.ts index 753ec8e028925..7d0c255392ee4 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.ts @@ -11,11 +11,11 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { ExternalRouteDeps } from '.'; import type { Space } from '../../../../common'; import { wrapError } from '../../../lib/errors'; -import { spaceSchema } from '../../../lib/space_schema'; +import { getSpaceSchema } from '../../../lib/space_schema'; import { createLicensedRouteHandler } from '../../lib'; export function initPutSpacesApi(deps: ExternalRouteDeps) { - const { router, getSpacesService } = deps; + const { router, getSpacesService, isServerless } = deps; router.put( { @@ -24,7 +24,7 @@ export function initPutSpacesApi(deps: ExternalRouteDeps) { params: schema.object({ id: schema.string(), }), - body: spaceSchema, + body: getSpaceSchema(isServerless), }, }, createLicensedRouteHandler(async (context, request, response) => { diff --git a/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts b/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts index 195db8148a378..874b5d4284958 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/update_objects_spaces.test.ts @@ -43,7 +43,7 @@ describe('update_objects_spaces', () => { const { savedObjects, savedObjectsClient } = createMockSavedObjectsService(spaces); coreStart.savedObjects = savedObjects; - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); @@ -67,6 +67,7 @@ describe('update_objects_spaces', () => { log, getSpacesService: () => spacesServiceStart, usageStatsServicePromise, + isServerless: false, }); const [[updateObjectsSpaces, updateObjectsSpacesRouteHandler]] = router.post.mock.calls; diff --git a/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts b/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts index 8f316d12209ba..09fdbe7896761 100644 --- a/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/internal/get_content_summary.test.ts @@ -45,7 +45,7 @@ describe('GET /internal/spaces/{spaceId}/content_summary', () => { const savedObjectsRepositoryMock = createMockSavedObjectsRepository(spacesSavedObjects); - const clientService = new SpacesClientService(jest.fn()); + const clientService = new SpacesClientService(jest.fn(), 'traditional'); clientService .setup({ config$: Rx.of(spacesConfig) }) .setClientRepositoryFactory(() => savedObjectsRepositoryMock); diff --git a/x-pack/plugins/spaces/server/saved_objects/mappings.ts b/x-pack/plugins/spaces/server/saved_objects/mappings.ts index 90d514e9fd10c..6f4313c3b1ed5 100644 --- a/x-pack/plugins/spaces/server/saved_objects/mappings.ts +++ b/x-pack/plugins/spaces/server/saved_objects/mappings.ts @@ -19,6 +19,9 @@ export const SpacesSavedObjectMappings = deepFreeze({ }, }, }, + solution: { + type: 'keyword', + }, }, } as const); diff --git a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts index b86bbf58f065c..1cdfa00d63238 100644 --- a/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts +++ b/x-pack/plugins/spaces/server/saved_objects/saved_objects_service.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { schema } from '@kbn/config-schema'; import type { CoreSetup } from '@kbn/core/server'; import { SpacesSavedObjectMappings, UsageStatsMappings } from './mappings'; @@ -30,6 +31,30 @@ export class SpacesSavedObjectsService { migrations: { '6.6.0': spaceMigrations.migrateTo660, }, + modelVersions: { + 1: { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + solution: { type: 'keyword' }, + }, + }, + ], + schemas: { + create: SpacesSavedObjectSchemas['8.8.0'].extends({ + solution: schema.maybe( + schema.oneOf([ + schema.literal('security'), + schema.literal('observability'), + schema.literal('search'), + schema.literal('classic'), + ]) + ), + }), + }, + }, + }, }); core.savedObjects.registerType({ diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts index 0f689cd492e11..600f6d1aa316c 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.test.ts @@ -42,6 +42,7 @@ describe('#getAll', () => { imageUrl: 'go-bots/predates/transformers', disabledFeatures: [], _reserved: true, + solution: 'search', bar: 'foo-bar', // an extra attribute that will be ignored during conversion }, }, @@ -80,6 +81,7 @@ describe('#getAll', () => { initials: 'FB', imageUrl: 'go-bots/predates/transformers', disabledFeatures: [], + solution: 'search', _reserved: true, }, { @@ -105,7 +107,13 @@ describe('#getAll', () => { } as any); const mockConfig = createMockConfig(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const actualSpaces = await client.getAll(); expect(actualSpaces).toEqual(expectedSpaces); @@ -117,11 +125,46 @@ describe('#getAll', () => { }); }); + test('strips solution property in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const [SOWithSolution] = savedObjects; + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.find.mockResolvedValue({ + saved_objects: [SOWithSolution], + } as any); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const [actualSpace] = await client.getAll(); + const [{ solution, ...expectedSpace }] = expectedSpaces; + + expect(actualSpace.solution).toBeUndefined(); + expect(actualSpace).toEqual(expectedSpace); + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: mockConfig.maxSpaces, + sortField: 'name.keyword', + }); + }); + test(`throws Boom.badRequest when an invalid purpose is provided'`, async () => { const mockDebugLogger = createMockDebugLogger(); const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); const mockConfig = createMockConfig(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect( client.getAll({ purpose: 'invalid_purpose' as GetAllSpacesPurpose }) ).rejects.toThrowErrorMatchingInlineSnapshot(`"unsupported space purpose: invalid_purpose"`); @@ -162,13 +205,64 @@ describe('#get', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(savedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const id = savedObject.id; const actualSpace = await client.get(id); expect(actualSpace).toEqual(expectedSpace); expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); }); + + test('strips solution property in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const id = savedObject.id; + const actualSpace = await client.get(id); + + expect(actualSpace.solution).toBeUndefined(); + expect(actualSpace).toEqual(expectedSpace); + }); + + test(`doesn't strip solution property in traditional build`, async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + const mockConfig = createMockConfig(); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + const actualSpace = await client.get(id); + + expect(actualSpace).toEqual({ ...expectedSpace, solution: 'search' }); + }); }); describe('#create', () => { @@ -219,7 +313,13 @@ describe('#create', () => { allowFeatureVisibility: true, }); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const actualSpace = await client.create(spaceToCreate); @@ -249,7 +349,13 @@ describe('#create', () => { allowFeatureVisibility: true, }); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect(client.create(spaceToCreate)).rejects.toThrowErrorMatchingInlineSnapshot( `"Unable to create Space, this exceeds the maximum number of spaces set by the xpack.spaces.maxSpaces setting"` @@ -263,6 +369,93 @@ describe('#create', () => { expect(mockCallWithRequestRepository.create).not.toHaveBeenCalled(); }); + test('throws bad request when solution property is provided in serverless build', async () => { + const maxSpaces = 5; + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.create.mockResolvedValue(savedObject); + mockCallWithRequestRepository.find.mockResolvedValue({ + total: maxSpaces - 1, + } as any); + + const mockConfig = createMockConfig({ + enabled: true, + maxSpaces, + allowFeatureVisibility: true, + }); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + + await expect( + client.create({ ...spaceToCreate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to create Space, solution property is forbidden in serverless"` + ); + + await expect( + client.create({ ...spaceToCreate, solution: 'search' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to create Space, solution property is forbidden in serverless"` + ); + + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: 0, + }); + expect(mockCallWithRequestRepository.create).not.toHaveBeenCalled(); + }); + + test('creates space when solution property is provided in traditional build', async () => { + const maxSpaces = 5; + const mockDebugLogger = createMockDebugLogger(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.create.mockResolvedValue({ + ...savedObject, + attributes: { ...(savedObject.attributes as Record), solution: 'search' }, + }); + mockCallWithRequestRepository.find.mockResolvedValue({ + total: maxSpaces - 1, + } as any); + + const mockConfig = createMockConfig({ + enabled: true, + maxSpaces, + allowFeatureVisibility: true, + }); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + + const actualSpace = await client.create({ ...spaceToCreate, solution: 'search' }); + + expect(actualSpace).toEqual({ ...expectedReturnedSpace, solution: 'search' }); + + expect(mockCallWithRequestRepository.find).toHaveBeenCalledWith({ + type: 'space', + page: 1, + perPage: 0, + }); + expect(mockCallWithRequestRepository.create).toHaveBeenCalledWith( + 'space', + { ...attributes, solution: 'search' }, + { + id, + } + ); + }); + describe('when config.allowFeatureVisibility is disabled', () => { test(`creates space without disabledFeatures`, async () => { const maxSpaces = 5; @@ -283,7 +476,8 @@ describe('#create', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const actualSpace = await client.create(spaceToCreate); @@ -318,7 +512,8 @@ describe('#create', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); await expect( @@ -377,7 +572,13 @@ describe('#update', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(savedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const id = savedObject.id; const actualSpace = await client.update(id, spaceToUpdate); @@ -386,6 +587,87 @@ describe('#update', () => { expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); }); + test('throws bad request when solution property is provided in serverless build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'serverless' + ); + const id = savedObject.id; + + await expect( + client.update(id, { ...spaceToUpdate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property is forbidden in serverless"` + ); + + await expect( + client.update(id, { ...spaceToUpdate, solution: 'search' }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property is forbidden in serverless"` + ); + + expect(mockCallWithRequestRepository.update).not.toHaveBeenCalled(); + + expect(mockCallWithRequestRepository.get).not.toHaveBeenCalled(); + }); + + test('throws bad request when solution property is undefined in traditional build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + + await expect( + client.update(id, { ...spaceToUpdate, solution: undefined }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"Unable to update Space, solution property cannot be empty"` + ); + + expect(mockCallWithRequestRepository.update).not.toHaveBeenCalled(); + + expect(mockCallWithRequestRepository.get).not.toHaveBeenCalled(); + }); + + test('updates space with solution property using callWithRequestRepository in traditional build', async () => { + const mockDebugLogger = createMockDebugLogger(); + const mockConfig = createMockConfig(); + const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); + mockCallWithRequestRepository.get.mockResolvedValue(savedObject); + + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); + const id = savedObject.id; + await client.update(id, { ...spaceToUpdate, solution: 'search' }); + + expect(mockCallWithRequestRepository.update).toHaveBeenCalledWith('space', id, { + ...attributes, + solution: 'search', + }); + expect(mockCallWithRequestRepository.get).toHaveBeenCalledWith('space', id); + }); + describe('when config.allowFeatureVisibility is disabled', () => { test(`updates space without disabledFeatures`, async () => { const mockDebugLogger = createMockDebugLogger(); @@ -401,7 +683,8 @@ describe('#update', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const id = savedObject.id; const actualSpace = await client.update(id, spaceToUpdate); @@ -425,7 +708,8 @@ describe('#update', () => { mockDebugLogger, mockConfig, mockCallWithRequestRepository, - [] + [], + 'traditional' ); const id = savedObject.id; @@ -473,7 +757,13 @@ describe('#delete', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(reservedSavedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await expect(client.delete(id)).rejects.toThrowErrorMatchingInlineSnapshot( `"The foo space cannot be deleted because it is reserved."` @@ -488,7 +778,13 @@ describe('#delete', () => { const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); mockCallWithRequestRepository.get.mockResolvedValue(notReservedSavedObject); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); await client.delete(id); @@ -504,7 +800,13 @@ describe('#disableLegacyUrlAliases', () => { const mockConfig = createMockConfig(); const mockCallWithRequestRepository = savedObjectsRepositoryMock.create(); - const client = new SpacesClient(mockDebugLogger, mockConfig, mockCallWithRequestRepository, []); + const client = new SpacesClient( + mockDebugLogger, + mockConfig, + mockCallWithRequestRepository, + [], + 'traditional' + ); const aliases = [ { targetSpace: 'space1', targetType: 'foo', sourceId: '123' }, { targetSpace: 'space2', targetType: 'bar', sourceId: '456' }, diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts index cc4058ad22485..536765e8dac47 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client.ts @@ -7,6 +7,7 @@ import Boom from '@hapi/boom'; +import type { BuildFlavor } from '@kbn/config/src/types'; import type { ISavedObjectsPointInTimeFinder, ISavedObjectsRepository, @@ -80,12 +81,17 @@ export interface ISpacesClient { * Client for interacting with spaces. */ export class SpacesClient implements ISpacesClient { + private isServerless = false; + constructor( private readonly debugLogger: (message: string) => void, private readonly config: ConfigType, private readonly repository: ISavedObjectsRepository, - private readonly nonGlobalTypeNames: string[] - ) {} + private readonly nonGlobalTypeNames: string[], + private readonly buildFlavour: BuildFlavor + ) { + this.isServerless = this.buildFlavour === 'serverless'; + } public async getAll(options: v1.GetAllSpacesOptions = {}): Promise { const { purpose = DEFAULT_PURPOSE } = options; @@ -130,10 +136,19 @@ export class SpacesClient implements ISpacesClient { ); } + if (this.isServerless && space.hasOwnProperty('solution')) { + throw Boom.badRequest('Unable to create Space, solution property is forbidden in serverless'); + } + + if (space.hasOwnProperty('solution') && !space.solution) { + throw Boom.badRequest('Unable to create Space, solution property cannot be empty'); + } + this.debugLogger(`SpacesClient.create(), using RBAC. Attempting to create space`); const id = space.id; const attributes = this.generateSpaceAttributes(space); + const createdSavedObject = await this.repository.create('space', attributes, { id }); this.debugLogger(`SpacesClient.create(), created space object`); @@ -148,6 +163,14 @@ export class SpacesClient implements ISpacesClient { ); } + if (this.isServerless && space.hasOwnProperty('solution')) { + throw Boom.badRequest('Unable to update Space, solution property is forbidden in serverless'); + } + + if (space.hasOwnProperty('solution') && !space.solution) { + throw Boom.badRequest('Unable to update Space, solution property cannot be empty'); + } + const attributes = this.generateSpaceAttributes(space); await this.repository.update('space', id, attributes); const updatedSavedObject = await this.repository.get('space', id); @@ -181,7 +204,7 @@ export class SpacesClient implements ISpacesClient { await this.repository.bulkUpdate(objectsToUpdate); } - private transformSavedObjectToSpace(savedObject: SavedObject): v1.Space { + private transformSavedObjectToSpace = (savedObject: SavedObject): v1.Space => { return { id: savedObject.id, name: savedObject.attributes.name ?? '', @@ -191,10 +214,11 @@ export class SpacesClient implements ISpacesClient { imageUrl: savedObject.attributes.imageUrl, disabledFeatures: savedObject.attributes.disabledFeatures ?? [], _reserved: savedObject.attributes._reserved, + ...(!this.isServerless ? { solution: savedObject.attributes.solution } : {}), } as v1.Space; - } + }; - private generateSpaceAttributes(space: v1.Space) { + private generateSpaceAttributes = (space: v1.Space) => { return { name: space.name, description: space.description, @@ -202,6 +226,7 @@ export class SpacesClient implements ISpacesClient { initials: space.initials, imageUrl: space.imageUrl, disabledFeatures: space.disabledFeatures, + ...(!this.isServerless && space.solution ? { solution: space.solution } : {}), }; - } + }; } diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts index 455387a816bd5..cedcdec858e55 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.test.ts @@ -20,7 +20,7 @@ const debugLogger = jest.fn(); describe('SpacesClientService', () => { describe('#setup', () => { it('allows a single repository factory to be set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const repositoryFactory = jest.fn(); @@ -32,7 +32,7 @@ describe('SpacesClientService', () => { }); it('allows a single client wrapper to be set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const clientWrapper = jest.fn(); @@ -46,7 +46,7 @@ describe('SpacesClientService', () => { describe('#start', () => { it('throws if config is not available', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); service.setup({ config$: new Rx.Observable() }); const coreStart = coreMock.createStart(); const start = service.start(coreStart); @@ -60,7 +60,7 @@ describe('SpacesClientService', () => { describe('without a custom repository factory or wrapper', () => { it('returns an instance of the spaces client using the scoped repository', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); service.setup({ config$: Rx.of(spacesConfig) }); const coreStart = coreMock.createStart(); @@ -78,7 +78,7 @@ describe('SpacesClientService', () => { }); it('uses the custom repository factory when set', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const customRepositoryFactory = jest.fn(); @@ -98,7 +98,7 @@ describe('SpacesClientService', () => { }); it('wraps the client in the wrapper when registered', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const wrapper = Symbol() as unknown as ISpacesClient; @@ -123,7 +123,7 @@ describe('SpacesClientService', () => { }); it('wraps the client in the wrapper when registered, using the custom repository factory when configured', () => { - const service = new SpacesClientService(debugLogger); + const service = new SpacesClientService(debugLogger, 'traditional'); const setup = service.setup({ config$: Rx.of(spacesConfig) }); const customRepositoryFactory = jest.fn(); diff --git a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts index 8a6c0eaab0d3b..26d4fef85ea6d 100644 --- a/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts +++ b/x-pack/plugins/spaces/server/spaces_client/spaces_client_service.ts @@ -7,6 +7,7 @@ import type { Observable } from 'rxjs'; +import type { BuildFlavor } from '@kbn/config/src/types'; import type { CoreStart, ISavedObjectsRepository, @@ -72,7 +73,10 @@ export class SpacesClientService { private clientWrapper?: SpacesClientWrapper; - constructor(private readonly debugLogger: (message: string) => void) {} + constructor( + private readonly debugLogger: (message: string) => void, + private readonly buildFlavour: BuildFlavor + ) {} public setup({ config$ }: SetupDeps): SpacesClientServiceSetup { config$.subscribe((nextConfig) => { @@ -117,7 +121,8 @@ export class SpacesClientService { this.debugLogger, this.config, this.repositoryFactory!(request, coreStart.savedObjects), - nonGlobalTypeNames + nonGlobalTypeNames, + this.buildFlavour ); if (this.clientWrapper) { return this.clientWrapper(request, baseClient); diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts index 694fb5b69e46a..8222b43018b2e 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts @@ -69,7 +69,7 @@ const createService = (serverBasePath: string = '') => { basePath: httpSetup.basePath, }); - const spacesClientService = new SpacesClientService(jest.fn()); + const spacesClientService = new SpacesClientService(jest.fn(), 'traditional'); spacesClientService.setup({ config$: Rx.of(spacesConfig), }); diff --git a/x-pack/test/spaces_api_integration/common/suites/create.ts b/x-pack/test/spaces_api_integration/common/suites/create.ts index b882e328cc3a2..c7aab659ce960 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.ts @@ -19,6 +19,7 @@ interface CreateTests { newSpace: CreateTest; alreadyExists: CreateTest; reservedSpecified: CreateTest; + solutionSpecified: CreateTest; } interface CreateTestDefinition { @@ -63,6 +64,17 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest) => { + expect(resp.body).to.eql({ + id: 'solution', + name: 'space with solution', + description: 'a description', + color: '#5c5959', + disabledFeatures: [], + solution: 'search', + }); + }; + const makeCreateTest = (describeFn: DescribeFn) => (description: string, { user = {}, spaceId, tests }: CreateTestDefinition) => { @@ -128,6 +140,24 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest { + it(`should return ${tests.solutionSpecified.statusCode}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .auth(user.username, user.password) + .send({ + name: 'space with solution', + id: 'solution', + description: 'a description', + color: '#5c5959', + solution: 'search', + disabledFeatures: [], + }) + .expect(tests.solutionSpecified.statusCode) + .then(tests.solutionSpecified.response); + }); + }); }); }); }; @@ -142,5 +172,6 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest { @@ -68,6 +69,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -87,6 +92,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -106,6 +115,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -125,6 +138,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); @@ -144,6 +161,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -163,6 +184,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -182,6 +207,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); @@ -201,6 +230,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 403, response: expectRbacForbiddenResponse, }, + solutionSpecified: { + statusCode: 403, + response: expectRbacForbiddenResponse, + }, }, }); }); diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts index 4d18c9b51dd55..000d6b8f2ebe7 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/create.ts @@ -19,6 +19,7 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext expectNewSpaceResult, expectConflictResponse, expectReservedSpecifiedResult, + expectSolutionSpecifiedResult, } = createTestSuiteFactory(esArchiver, supertestWithoutAuth); describe('create', () => { @@ -45,6 +46,10 @@ export default function createSpacesOnlySuite({ getService }: FtrProviderContext statusCode: 200, response: expectReservedSpecifiedResult, }, + solutionSpecified: { + statusCode: 200, + response: expectSolutionSpecifiedResult, + }, }, }); }); From ba463ba62cac8a7c86a55aff3acc3cb01763902a Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Mon, 27 May 2024 12:36:23 +0200 Subject: [PATCH 07/59] [Index Management] Add config flag for semantic text (#184127) ## Summary This adds a config flag to semantic text so we can toggle it on/off easily. --- .../test_suites/core_plugins/rendering.ts | 1 + .../helpers/setup_environment.tsx | 1 + .../public/application/app_context.tsx | 1 + .../details_page/details_page_mappings_content.tsx | 13 +++---------- x-pack/plugins/index_management/public/plugin.ts | 3 +++ x-pack/plugins/index_management/public/types.ts | 3 +++ x-pack/plugins/index_management/server/config.ts | 5 +++++ 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/test/plugin_functional/test_suites/core_plugins/rendering.ts b/test/plugin_functional/test_suites/core_plugins/rendering.ts index 62cffabc1b82c..25388c7829edd 100644 --- a/test/plugin_functional/test_suites/core_plugins/rendering.ts +++ b/test/plugin_functional/test_suites/core_plugins/rendering.ts @@ -293,6 +293,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) { 'xpack.index_management.editableIndexSettings (any)', 'xpack.index_management.enableDataStreamsStorageColumn (any)', 'xpack.index_management.enableMappingsSourceFieldSection (any)', + 'xpack.index_management.dev.enableSemanticText (boolean)', 'xpack.license_management.ui.enabled (boolean)', 'xpack.maps.preserveDrawingBuffer (boolean)', 'xpack.maps.showMapsInspectorAdapter (boolean)', diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index 99fa4075d2d94..5c448aef1790c 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -86,6 +86,7 @@ const appDependencies = { enableDataStreamsStorageColumn: true, enableMappingsSourceFieldSection: true, enableTogglingDataRetention: true, + enableSemanticText: false, }, } as any; diff --git a/x-pack/plugins/index_management/public/application/app_context.tsx b/x-pack/plugins/index_management/public/application/app_context.tsx index 8ee80e2f8f55f..964a0e098c15e 100644 --- a/x-pack/plugins/index_management/public/application/app_context.tsx +++ b/x-pack/plugins/index_management/public/application/app_context.tsx @@ -64,6 +64,7 @@ export interface AppDependencies { enableDataStreamsStorageColumn: boolean; enableMappingsSourceFieldSection: boolean; enableTogglingDataRetention: boolean; + enableSemanticText: boolean; }; history: ScopedHistory; setBreadcrumbs: (type: IndexManagementBreadcrumb, additionalBreadcrumb?: EuiBreadcrumb) => void; diff --git a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx index 71181796f6cbf..b126f5b960a47 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx @@ -62,22 +62,15 @@ export const DetailsPageMappingsContent: FunctionComponent<{ showAboutMappings: boolean; jsonData: any; refetchMapping: () => void; - isSemanticTextEnabled?: boolean; -}> = ({ - index, - data, - jsonData, - refetchMapping, - showAboutMappings, - isSemanticTextEnabled = false, -}) => { +}> = ({ index, data, jsonData, refetchMapping, showAboutMappings }) => { const { services: { extensionsService }, core: { getUrlForApp }, plugins: { ml }, url, + config, } = useAppContext(); - + const { enableSemanticText: isSemanticTextEnabled } = config; const [errorsInTrainedModelDeployment, setErrorsInTrainedModelDeployment] = useState( [] ); diff --git a/x-pack/plugins/index_management/public/plugin.ts b/x-pack/plugins/index_management/public/plugin.ts index 4e6947b56ba9e..a94fca4f6198f 100644 --- a/x-pack/plugins/index_management/public/plugin.ts +++ b/x-pack/plugins/index_management/public/plugin.ts @@ -46,6 +46,7 @@ export class IndexMgmtUIPlugin isIndexManagementUiEnabled: boolean; enableMappingsSourceFieldSection: boolean; enableTogglingDataRetention: boolean; + enableSemanticText: boolean; }; constructor(ctx: PluginInitializerContext) { @@ -62,6 +63,7 @@ export class IndexMgmtUIPlugin enableDataStreamsStorageColumn, enableMappingsSourceFieldSection, enableTogglingDataRetention, + dev: { enableSemanticText }, } = ctx.config.get(); this.config = { isIndexManagementUiEnabled, @@ -72,6 +74,7 @@ export class IndexMgmtUIPlugin enableDataStreamsStorageColumn: enableDataStreamsStorageColumn ?? true, enableMappingsSourceFieldSection: enableMappingsSourceFieldSection ?? true, enableTogglingDataRetention: enableTogglingDataRetention ?? true, + enableSemanticText: enableSemanticText ?? false, }; } diff --git a/x-pack/plugins/index_management/public/types.ts b/x-pack/plugins/index_management/public/types.ts index e6a3b9a611026..30df6157abd8b 100644 --- a/x-pack/plugins/index_management/public/types.ts +++ b/x-pack/plugins/index_management/public/types.ts @@ -54,4 +54,7 @@ export interface ClientConfigType { enableDataStreamsStorageColumn?: boolean; enableMappingsSourceFieldSection?: boolean; enableTogglingDataRetention?: boolean; + dev: { + enableSemanticText?: boolean; + }; } diff --git a/x-pack/plugins/index_management/server/config.ts b/x-pack/plugins/index_management/server/config.ts index 4348d7ac2a774..c213cf5b8a87f 100644 --- a/x-pack/plugins/index_management/server/config.ts +++ b/x-pack/plugins/index_management/server/config.ts @@ -35,6 +35,8 @@ const schemaLatest = schema.object( dev: schema.object({ // deprecated as unused after index details page has been implemented enableIndexDetailsPage: schema.boolean({ defaultValue: false }), + // deprecate as unused after semantic text is enabled everywhere + enableSemanticText: schema.boolean({ defaultValue: false }), }), enableIndexStats: offeringBasedSchema({ // Index stats information is disabled in serverless; refer to the serverless.yml file as the source of truth @@ -69,6 +71,9 @@ const schemaLatest = schema.object( const configLatest: PluginConfigDescriptor = { exposeToBrowser: { ui: true, + dev: { + enableSemanticText: true, + }, enableIndexActions: true, enableLegacyTemplates: true, enableIndexStats: true, From abd55fb274a1a2145d73115be82eec1816e7b0a1 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 May 2024 13:20:59 +0200 Subject: [PATCH 08/59] [ES|QL] Recognize transformational commands with ast parsing (#184291) ## Summary Closes https://github.com/elastic/kibana/issues/184139 It uses ast parsing to recognize if the query has a transformational command. Although I added `metrics` on the list it wont work for it until we support it on our ast system. I am just adding here proactively. `Stats` and `keep` should work as expected though image ### Checklist - [ ] [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 --- packages/kbn-esql-utils/index.ts | 1 + packages/kbn-esql-utils/src/index.ts | 1 + .../src/utils/query_parsing_helpers.test.ts | 22 +++++++++++++++++++ .../src/utils/query_parsing_helpers.ts | 7 ++++++ .../application/main/hooks/use_esql_mode.ts | 13 ++++------- .../public/layout/helpers.ts | 5 ++--- 6 files changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/kbn-esql-utils/index.ts b/packages/kbn-esql-utils/index.ts index 31fa7eb3a14c8..306d187f1ef07 100644 --- a/packages/kbn-esql-utils/index.ts +++ b/packages/kbn-esql-utils/index.ts @@ -9,6 +9,7 @@ export { getESQLAdHocDataview, getIndexPatternFromESQLQuery, + hasTransformationalCommand, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, getIndexForESQLQuery, diff --git a/packages/kbn-esql-utils/src/index.ts b/packages/kbn-esql-utils/src/index.ts index a48765e8c0489..18fc938b5eee5 100644 --- a/packages/kbn-esql-utils/src/index.ts +++ b/packages/kbn-esql-utils/src/index.ts @@ -14,6 +14,7 @@ export { getIndexPatternFromESQLQuery, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, + hasTransformationalCommand, } from './utils/query_parsing_helpers'; export { appendToESQLQuery, appendWhereClauseToESQLQuery } from './utils/append_to_query'; export { getESQLQueryColumns, getESQLQueryColumnsRaw, getESQLResults } from './utils/run_query'; diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts index 9e7686127af02..3cb3c3171942d 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.test.ts @@ -10,6 +10,7 @@ import { getIndexPatternFromESQLQuery, getLimitFromESQLQuery, removeDropCommandsFromESQLQuery, + hasTransformationalCommand, } from './query_parsing_helpers'; describe('esql query helpers', () => { @@ -95,4 +96,25 @@ describe('esql query helpers', () => { ).toBe('from a | keep c '); }); }); + + describe('hasTransformationalCommand', () => { + it('should return false for non transformational command', () => { + expect(hasTransformationalCommand('from a | eval b = 1')).toBeFalsy(); + }); + + it('should return true for stats', () => { + expect(hasTransformationalCommand('from a | stats count() as total by a=b')).toBeTruthy(); + }); + + it('should return true for keep', () => { + expect(hasTransformationalCommand('from a | keep field1, field2')).toBeTruthy(); + }); + + it('should return false for commented out transformational command', () => { + expect( + hasTransformationalCommand(`from logstash-* + // | stats var0 = avg(bytes) by geo.dest`) + ).toBeFalsy(); + }); + }); }); diff --git a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts index af6c931406bac..994551930cc5a 100644 --- a/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts +++ b/packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts @@ -18,6 +18,13 @@ export function getIndexPatternFromESQLQuery(esql?: string) { return indices?.map((index) => index.text).join(','); } +// For ES|QL we consider the following commands as transformational commands +export function hasTransformationalCommand(esql?: string) { + const transformationalCommands = ['stats', 'keep', 'metrics']; + const { ast } = getAstAndSyntaxErrors(esql); + return transformationalCommands.some((command) => ast.find(({ name }) => name === command)); +} + export function getLimitFromESQLQuery(esql: string): number { const limitCommands = esql.match(new RegExp(/LIMIT\s[0-9]+/, 'ig')); if (!limitCommands) { diff --git a/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts index a907b1e796c87..2f3c99763fb04 100644 --- a/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts +++ b/src/plugins/discover/public/application/main/hooks/use_esql_mode.ts @@ -8,6 +8,7 @@ import { isEqual } from 'lodash'; import { isOfAggregateQueryType, getAggregateQueryMode } from '@kbn/es-query'; +import { hasTransformationalCommand } from '@kbn/esql-utils'; import { useCallback, useEffect, useRef } from 'react'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import { switchMap } from 'rxjs'; @@ -17,9 +18,6 @@ import { getValidViewMode } from '../utils/get_valid_view_mode'; import { FetchStatus } from '../../types'; const MAX_NUM_OF_COLUMNS = 50; -// For ES|QL we want in case of the following commands to display a table view, otherwise display a document view -const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; - /** * Hook to take care of ES|QL state transformations when a new result is returned * If necessary this is setting displayed columns and selected data view @@ -71,12 +69,9 @@ export function useEsqlMode({ const hasResults = Boolean(next.result?.length); let queryHasTransformationalCommands = false; if ('esql' in query) { - TRANSFORMATIONAL_COMMANDS.forEach((command: string) => { - if (query.esql.toLowerCase().includes(command)) { - queryHasTransformationalCommands = true; - return; - } - }); + if (hasTransformationalCommand(query.esql)) { + queryHasTransformationalCommands = true; + } } if (isEsqlQuery) { diff --git a/src/plugins/unified_histogram/public/layout/helpers.ts b/src/plugins/unified_histogram/public/layout/helpers.ts index 0f6e898163779..d65ddb7763d28 100644 --- a/src/plugins/unified_histogram/public/layout/helpers.ts +++ b/src/plugins/unified_histogram/public/layout/helpers.ts @@ -7,9 +7,8 @@ */ import { AggregateQuery } from '@kbn/es-query'; - -const TRANSFORMATIONAL_COMMANDS = ['stats', 'keep']; +import { hasTransformationalCommand } from '@kbn/esql-utils'; export const shouldDisplayHistogram = (query: AggregateQuery) => { - return !TRANSFORMATIONAL_COMMANDS.some((command) => query.esql.toLowerCase().includes(command)); + return !hasTransformationalCommand(query.esql); }; From a2c2f654eb31aaf3142451cd19b6de24f4976d84 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 27 May 2024 13:21:14 +0200 Subject: [PATCH 09/59] [ES|QL] Filter by brushing a date histogram (#184012) ## Summary Part of https://github.com/elastic/kibana/issues/183425 Adds support of brushing an ES|QL chart. It updates the timepicker. ![meow (2)](https://github.com/elastic/kibana/assets/17003240/05ac4e92-ea88-4ab7-93f7-3e7c9e300176) ### Checklist - [ ] [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 --------- Co-authored-by: Marco Liberati --- .../public/components/xy_chart.tsx | 14 +++- .../data/common/search/expressions/esql.ts | 4 +- .../search/strategies/esql_search/types.ts | 1 + .../create_filters_from_range_select.test.ts | 64 +++++++++++++++++-- .../create_filters_from_range_select.ts | 50 ++++++++++++--- .../public/actions/select_range_action.ts | 2 + .../layout/discover_histogram_layout.tsx | 4 ++ .../text_based/text_based_languages.test.ts | 41 ++++++++++++ .../text_based/text_based_languages.tsx | 14 ++++ .../lens/public/embeddable/embeddable.tsx | 4 +- 10 files changed, 176 insertions(+), 22 deletions(-) diff --git a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx index ed4f6ffad6a73..660e2c95bed2f 100644 --- a/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx +++ b/src/plugins/chart_expressions/expression_xy/public/components/xy_chart.tsx @@ -37,6 +37,7 @@ import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { PaletteRegistry } from '@kbn/coloring'; import { RenderMode } from '@kbn/expressions-plugin/common'; +import { ESQL_TABLE_TYPE } from '@kbn/data-plugin/common'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { EmptyPlaceholder, LegendToggle } from '@kbn/charts-plugin/public'; import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'; @@ -403,6 +404,7 @@ export function XYChart({ const defaultXScaleType = isTimeViz ? XScaleTypes.TIME : XScaleTypes.ORDINAL; const isHistogramViz = dataLayers.every((l) => l.isHistogram); + const isEsqlMode = dataLayers.some((l) => l.table?.meta?.type === ESQL_TABLE_TYPE); const hasBars = dataLayers.some((l) => l.seriesType === SeriesTypes.BAR); const { baseDomain: rawXDomain, extendedDomain: xDomain } = getXDomain( @@ -651,7 +653,12 @@ export function XYChart({ : undefined; const xAxisColumnIndex = table.columns.findIndex((el) => el.id === xAccessor); - const context: BrushEvent['data'] = { range: [min, max], table, column: xAxisColumnIndex }; + const context: BrushEvent['data'] = { + range: [min, max], + table, + column: xAxisColumnIndex, + ...(isEsqlMode ? { timeFieldName: table.columns[xAxisColumnIndex].name } : {}), + }; onSelectRange(context); }; @@ -779,7 +786,7 @@ export function XYChart({ formattedDatatables, xAxisFormatter, formatFactory, - interactive && !args.detailedTooltip + interactive && !args.detailedTooltip && !isEsqlMode )} customTooltip={ args.detailedTooltip @@ -855,8 +862,9 @@ export function XYChart({ allowBrushingLastHistogramBin={isTimeViz} rotation={shouldRotate ? 90 : 0} xDomain={xDomain} + // enable brushing only for time charts, for both ES|QL and DSL queries onBrushEnd={interactive ? (brushHandler as BrushEndListener) : undefined} - onElementClick={interactive ? clickHandler : undefined} + onElementClick={interactive && !isEsqlMode ? clickHandler : undefined} legendAction={ interactive ? getLegendAction( diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts index 6c3929f201458..a4d546f349f37 100644 --- a/src/plugins/data/common/search/expressions/esql.ts +++ b/src/plugins/data/common/search/expressions/esql.ts @@ -22,7 +22,7 @@ import type { ESQLSearchReponse, ESQLSearchParams } from '@kbn/es-types'; import { ESQL_LATEST_VERSION } from '@kbn/esql-utils'; import { getEsQueryConfig } from '../../es_query'; import { getTime } from '../../query'; -import { ESQL_ASYNC_SEARCH_STRATEGY, KibanaContext } from '..'; +import { ESQL_ASYNC_SEARCH_STRATEGY, KibanaContext, ESQL_TABLE_TYPE } from '..'; import { UiSettingsCommon } from '../..'; type Input = KibanaContext | null; @@ -270,7 +270,7 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => { return { type: 'datatable', meta: { - type: 'es_ql', + type: ESQL_TABLE_TYPE, }, columns: allColumns, rows, diff --git a/src/plugins/data/common/search/strategies/esql_search/types.ts b/src/plugins/data/common/search/strategies/esql_search/types.ts index 7d69e8379a250..0ceb71db9d496 100644 --- a/src/plugins/data/common/search/strategies/esql_search/types.ts +++ b/src/plugins/data/common/search/strategies/esql_search/types.ts @@ -8,3 +8,4 @@ export const ESQL_SEARCH_STRATEGY = 'esql'; export const ESQL_ASYNC_SEARCH_STRATEGY = 'esql_async'; +export const ESQL_TABLE_TYPE = 'es_ql'; diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts index bee22e07ab144..74cdb28d7400c 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts @@ -8,7 +8,10 @@ import moment from 'moment'; -import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select'; +import { + createFiltersFromRangeSelectAction, + type RangeSelectDataContext, +} from './create_filters_from_range_select'; import { DataViewsContract } from '@kbn/data-views-plugin/common'; import { dataPluginMock } from '../../mocks'; @@ -20,12 +23,8 @@ import { RangeFilter } from '@kbn/es-query'; describe('brushEvent', () => { const DAY_IN_MS = 24 * 60 * 60 * 1000; const JAN_01_2014 = 1388559600000; - let baseEvent: { - table: any; - column: number; - range: number[]; - timeFieldName?: string; - }; + let baseEvent: RangeSelectDataContext; + let esqlEventContext: RangeSelectDataContext; const mockField = { name: 'time', @@ -82,6 +81,28 @@ describe('brushEvent', () => { }, range: [], }; + + esqlEventContext = { + column: 0, + query: { esql: 'FROM indexPatternId | limit 10' }, + table: { + type: 'datatable', + meta: { + type: 'es_ql', + }, + columns: [ + { + id: '1', + name: '1', + meta: { + type: 'date', + }, + }, + ], + rows: [], + }, + range: [], + }; }); test('should be a function', () => { @@ -197,4 +218,33 @@ describe('brushEvent', () => { } }); }); + + describe('handles an event for an ES_QL query', () => { + afterAll(() => { + esqlEventContext.range = []; + }); + + test('by ignoring the event when range does not span at least 2 values', async () => { + esqlEventContext.range = [JAN_01_2014]; + const filter = await createFiltersFromRangeSelectAction(esqlEventContext); + expect(filter).toEqual([]); + }); + + test('by creating a new filter', async () => { + const rangeBegin = JAN_01_2014; + const rangeEnd = rangeBegin + DAY_IN_MS; + esqlEventContext.range = [rangeBegin, rangeEnd]; + const filter = await createFiltersFromRangeSelectAction(esqlEventContext); + + expect(filter).toBeDefined(); + + if (filter.length) { + const rangeFilter = filter[0] as RangeFilter; + expect(rangeFilter.meta.index).toBeUndefined(); + expect(rangeFilter.query.range['1'].gte).toBe(moment(rangeBegin).toISOString()); + expect(rangeFilter.query.range['1'].lt).toBe(moment(rangeEnd).toISOString()); + expect(rangeFilter.query.range['1']).toHaveProperty('format', 'strict_date_optional_time'); + } + }); + }); }); diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts index c3acc8b2a5106..c5a6d86e20fb2 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts @@ -9,18 +9,57 @@ import { last } from 'lodash'; import moment from 'moment'; import { Datatable } from '@kbn/expressions-plugin/common'; +import { type AggregateQuery, isOfAggregateQueryType } from '@kbn/es-query'; +import { DataViewField } from '@kbn/data-views-plugin/public'; import { buildRangeFilter, DataViewFieldBase, RangeFilterParams } from '@kbn/es-query'; import { getIndexPatterns, getSearchService } from '../../services'; import { AggConfigSerialized } from '../../../common/search/aggs'; import { mapAndFlattenFilters } from '../../query'; -interface RangeSelectDataContext { +export interface RangeSelectDataContext { table: Datatable; column: number; range: number[]; timeFieldName?: string; + query?: AggregateQuery; } +const getParameters = async (event: RangeSelectDataContext) => { + const column: Record = event.table.columns[event.column]; + // Handling of the ES|QL datatable + if (isOfAggregateQueryType(event.query)) { + const field = new DataViewField({ + name: column.name, + type: column.meta?.type ?? 'unknown', + esTypes: column.meta?.esType ? ([column.meta.esType] as string[]) : undefined, + searchable: true, + aggregatable: false, + }); + + return { + field, + indexPattern: undefined, + }; + } + if (column.meta && 'sourceParams' in column.meta) { + const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; + const indexPattern = await getIndexPatterns().get(indexPatternId); + const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ + aggConfigs as AggConfigSerialized, + ]); + const aggConfig = aggConfigsInstance.aggs[0]; + const field: DataViewFieldBase = aggConfig.params.field; + return { + field, + indexPattern, + }; + } + return { + field: undefined, + indexPattern: undefined, + }; +}; + export async function createFiltersFromRangeSelectAction(event: RangeSelectDataContext) { const column: Record = event.table.columns[event.column]; @@ -28,13 +67,7 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectDataC return []; } - const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; - const indexPattern = await getIndexPatterns().get(indexPatternId); - const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ - aggConfigs as AggConfigSerialized, - ]); - const aggConfig = aggConfigsInstance.aggs[0]; - const field: DataViewFieldBase = aggConfig.params.field; + const { field, indexPattern } = await getParameters(event); if (!field || event.range.length <= 1) { return []; @@ -57,6 +90,5 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectDataC if (isDate) { range.format = 'strict_date_optional_time'; } - return mapAndFlattenFilters([buildRangeFilter(field, range, indexPattern)]); } diff --git a/src/plugins/data/public/actions/select_range_action.ts b/src/plugins/data/public/actions/select_range_action.ts index 921273ff4abd9..e8a65b049fd44 100644 --- a/src/plugins/data/public/actions/select_range_action.ts +++ b/src/plugins/data/public/actions/select_range_action.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { AggregateQuery } from '@kbn/es-query'; import { Datatable } from '@kbn/expressions-plugin/public'; import { UiActionsActionDefinition, UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { APPLY_FILTER_TRIGGER } from '../triggers'; @@ -20,6 +21,7 @@ export interface SelectRangeActionContext { column: number; range: number[]; timeFieldName?: string; + query?: AggregateQuery; }; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx index 68585d7faf5c0..1d24fdcd1aa81 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_histogram_layout.tsx @@ -10,6 +10,7 @@ import React, { useCallback, useMemo } from 'react'; import { UnifiedHistogramContainer } from '@kbn/unified-histogram-plugin/public'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; +import { ESQL_TABLE_TYPE } from '@kbn/data-plugin/common'; import type { Datatable } from '@kbn/expressions-plugin/common'; import { useDiscoverHistogram } from './use_discover_histogram'; import { type DiscoverMainContentProps, DiscoverMainContent } from './discover_main_content'; @@ -61,6 +62,9 @@ export const DiscoverHistogramLayout = ({ type: 'datatable' as 'datatable', rows: datatable.result!.map((r) => r.raw), columns: datatable.esqlQueryColumns || [], + meta: { + type: ESQL_TABLE_TYPE, + }, }; } }, [datatable, isEsqlMode]); diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts index d15723e074667..8904031615bcf 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.test.ts @@ -835,6 +835,47 @@ describe('Textbased Data Source', () => { isBucketed: false, hasTimeShift: false, hasReducedTimeRange: false, + scale: 'ratio', + }); + }); + + it('should get an operation for col2', () => { + const state = { + layers: { + a: { + columns: [ + { + columnId: 'col1', + fieldName: 'Test 1', + meta: { + type: 'number', + }, + }, + { + columnId: 'col2', + fieldName: 'Test 2', + meta: { + type: 'date', + }, + }, + ], + index: 'foo', + }, + }, + } as unknown as TextBasedPrivateState; + + publicAPI = TextBasedDatasource.getPublicAPI({ + state, + layerId: 'a', + indexPatterns, + }); + expect(publicAPI.getOperationForColumnId('col2')).toEqual({ + label: 'Test 2', + dataType: 'date', + isBucketed: true, + hasTimeShift: false, + hasReducedTimeRange: false, + scale: 'interval', }); }); diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index 9f5b44e660bd3..85d622380695c 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -30,6 +30,7 @@ import { DatasourceDimensionTriggerProps, DataSourceInfo, UserMessage, + OperationMetadata, } from '../../types'; import { generateId } from '../../id_generator'; import type { @@ -533,6 +534,18 @@ export function getTextBasedDatasource({ const layer = state.layers[layerId]; const column = layer?.columns?.find((c) => c.columnId === columnId); const columnLabelMap = TextBasedDatasource.uniqueLabels(state, indexPatterns); + let scale: OperationMetadata['scale'] = 'ordinal'; + switch (column?.meta?.type) { + case 'date': + scale = 'interval'; + break; + case 'number': + scale = 'ratio'; + break; + default: + scale = 'ordinal'; + break; + } if (column) { return { @@ -542,6 +555,7 @@ export function getTextBasedDatasource({ inMetricDimension: column.inMetricDimension, hasTimeShift: false, hasReducedTimeRange: false, + scale, }; } return null; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index daf1d078894e9..366b631f7f54b 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -1148,7 +1148,7 @@ export class Embeddable handleEvent={this.handleEvent} onData$={this.updateActiveData} onRender$={this.onRender} - interactive={!input.disableTriggers && !this.isTextBasedLanguage()} + interactive={!input.disableTriggers} renderMode={input.renderMode} syncColors={input.syncColors} syncTooltips={input.syncTooltips} @@ -1369,6 +1369,7 @@ export class Embeddable } else if (isLensTableRowContextMenuClickEvent(event)) { eventHandler = this.input.onTableRowClick; } + const esqlQuery = this.isTextBasedLanguage() ? this.savedVis?.state.query : undefined; eventHandler?.({ ...event.data, @@ -1384,6 +1385,7 @@ export class Embeddable ...event.data, timeFieldName: event.data.timeFieldName || inferTimeField(this.deps.data.datatableUtilities, event), + query: esqlQuery, }, embeddable: this, }); From 57b255552b43499ab408a933dba08b054f770530 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 27 May 2024 13:29:54 +0200 Subject: [PATCH 10/59] Update doc comment for `offeringBasedSchema` (#184223) ## Summary Updates the doc comment to callout that this utility should only be used to validate Kibana config. --- .../src/helpers/offering_based_schema.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/kbn-config-schema/src/helpers/offering_based_schema.ts b/packages/kbn-config-schema/src/helpers/offering_based_schema.ts index 09ddfc3a75644..b4ccaf57330f2 100644 --- a/packages/kbn-config-schema/src/helpers/offering_based_schema.ts +++ b/packages/kbn-config-schema/src/helpers/offering_based_schema.ts @@ -12,6 +12,16 @@ import { Type, TypeOptions } from '../types'; /** * Helper to apply different validations depending on whether Kibana is running the Serverless or Traditional offering. * + * @remark This utility is intended to be used for Kibana YAML-based configuration validation only! Using it in other + * contexts will lead to only `traditional` validation being used. + * + * If you want to switch schemas based on the offering in other contexts do the following: + * + * ```ts + * // env is passed to your plugin constructor + * const schema = env.packageInfo.buildFlavor === 'serverless' ? baseSchema.extend(a) : baseSchema.extend(b); + * ``` + * * @example Only allow the setting on Serverless * const config = schema.object({ * myProp: offeringBasedSchema({ serverless: schema.boolean({ defaultValue: true }) }), From dc46dfc2091eb2c30bb7d2d368ebae348bfe1d39 Mon Sep 17 00:00:00 2001 From: Tre Date: Mon, 27 May 2024 13:10:41 +0100 Subject: [PATCH 11/59] [ML] Add AD job validation UI tests (#179358) ## Summary - Add job wizard validation suite - Add job wizard model change annotation recommendation callout test subject - Add job wizard model time picker test subject using `popperProps` - Add test subjects for two steps: Time Range and Job Details - Add more methods to job wizard common service --------- Co-authored-by: Dima Arnautov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Robert Oskamp --- .../components/callout/callout.tsx | 2 +- .../common/components/time_range_picker.tsx | 6 ++ .../annotations/annotations_switch.tsx | 1 + .../pages/new_job/wizard_horizontal_steps.tsx | 6 ++ .../single_metric_job.ts | 85 +++++++++++++++++++ .../test/functional/services/ml/common_ui.ts | 4 + .../ml/data_frame_analytics_creation.ts | 4 +- .../services/ml/job_wizard_common.ts | 74 ++++++++++++++++ 8 files changed, 179 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/callout/callout.tsx b/x-pack/plugins/ml/public/application/components/callout/callout.tsx index b2c588377d232..8c60f87ef2427 100644 --- a/x-pack/plugins/ml/public/application/components/callout/callout.tsx +++ b/x-pack/plugins/ml/public/application/components/callout/callout.tsx @@ -55,7 +55,7 @@ const Message: FC> = ({ text, url }) => ( export const Callout: FC = ({ heading, status, text, url }) => ( <> = ({ setTimeRange, timeRange }) => { fullWidth={true} startDateControl={ = ({ setTimeRange, timeRange }) => { } endDateControl={ { {showCallOut && ( = ({ defaultMessage: 'Time range', }), ...createStepProps(WIZARD_STEPS.TIME_RANGE), + 'data-test-subj': 'mlJobWizardTimeRangeStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.pickFieldsTitle', { defaultMessage: 'Choose fields', }), ...createStepProps(WIZARD_STEPS.PICK_FIELDS), + 'data-test-subj': 'mlJobWizardPickFieldsStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.jobDetailsTitle', { defaultMessage: 'Job details', }), ...createStepProps(WIZARD_STEPS.JOB_DETAILS), + 'data-test-subj': 'mlJobWizardJobDetailsStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.validationTitle', { defaultMessage: 'Validation', }), ...createStepProps(WIZARD_STEPS.VALIDATION), + 'data-test-subj': 'mlJobWizardValidationStep', }, { title: i18n.translate('xpack.ml.newJob.wizard.step.summaryTitle', { defaultMessage: 'Summary', }), ...createStepProps(WIZARD_STEPS.SUMMARY), + 'data-test-subj': 'mlJobWizardSummaryStep', }, ]; @@ -75,6 +80,7 @@ export const WizardHorizontalSteps: FC = ({ defaultMessage: 'Configure datafeed', }), ...createStepProps(WIZARD_STEPS.ADVANCED_CONFIGURE_DATAFEED), + 'data-test-subj': 'mlJobWizardAdvancedStep', }); } diff --git a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts index 8b29adf3d5384..957ac090e1ade 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection_jobs/single_metric_job.ts @@ -388,5 +388,90 @@ export default function ({ getService }: FtrProviderContext) { ); await ml.api.assertNoJobResultsExist(jobIdClone); }); + + it('job cloning with too short of a job creation time range results in validation callouts', async () => { + await ml.testExecution.logTestStep('job cloning loads the job management page'); + await ml.navigation.navigateToMl(); + await ml.navigation.navigateToJobManagement(); + + await ml.testExecution.logTestStep(`cloning job: [${jobId}]`); + await ml.jobTable.clickCloneJobAction(jobId); + + await ml.testExecution.logTestStep('job cloning displays the time range step'); + await ml.jobWizardCommon.assertTimeRangeSectionExists(); + + await ml.testExecution.logTestStep('job cloning sets the time range'); + await ml.jobWizardCommon.clickUseFullDataButton( + 'Feb 7, 2016 @ 00:00:00.000', + 'Feb 11, 2016 @ 23:59:54.000' + ); + + await ml.jobWizardCommon.goToTimeRangeStep(); + + const { startDate: origStartDate } = await ml.jobWizardCommon.getSelectedDateRange(); + + await ml.testExecution.logTestStep('calculate the new end date'); + const shortDurationEndDate: string = `${origStartDate?.split(':', 1)[0]}:01:00.000`; + + await ml.testExecution.logTestStep('set the new end date'); + await ml.jobWizardCommon.setTimeRange({ endTime: shortDurationEndDate }); + + // assert time is set as expected + await ml.jobWizardCommon.assertDateRangeSelection( + origStartDate as string, + shortDurationEndDate + ); + + await ml.jobWizardCommon.advanceToPickFieldsSection(); + await ml.jobWizardCommon.advanceToJobDetailsSection(); + await ml.jobWizardCommon.assertJobIdInputExists(); + await ml.jobWizardCommon.setJobId(`${jobIdClone}-again`); + await ml.jobWizardCommon.advanceToValidationSection(); + await ml.jobWizardCommon.assertValidationCallouts([ + 'mlValidationCallout warning', + 'mlValidationCallout error', + ]); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout warning', + /Time range\s*The selected or available time range might be too short/ + ); + + await ml.jobWizardCommon.goToTimeRangeStep(); + await ml.jobWizardCommon.clickUseFullDataButton( + 'Feb 7, 2016 @ 00:00:00.000', + 'Feb 11, 2016 @ 23:59:54.000' + ); + await ml.jobWizardCommon.goToValidationStep(); + await ml.jobWizardCommon.assertValidationCallouts(['mlValidationCallout success']); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout success', + /Time range\s*Valid and long enough to model patterns in the data/ + ); + }); + + it('job creation and toggling model change annotation triggers enable annotation recommendation callout', async () => { + await ml.jobWizardCommon.goToJobDetailsStep(); + await ml.jobWizardCommon.ensureAdvancedSectionOpen(); + + await ml.commonUI.toggleSwitchIfNeeded('mlJobWizardSwitchAnnotations', false); + await ml.jobWizardCommon.assertAnnotationRecommendationCalloutVisible(); + + await ml.commonUI.toggleSwitchIfNeeded('mlJobWizardSwitchAnnotations', true); + await ml.jobWizardCommon.assertAnnotationRecommendationCalloutVisible(false); + }); + + it('job creation memory limit too large results in validation callout', async () => { + await ml.jobWizardCommon.goToJobDetailsStep(); + + const tooLarge = '100000000MB'; + await ml.jobWizardCommon.setModelMemoryLimit(tooLarge); + + await ml.jobWizardCommon.advanceToValidationSection(); + await ml.jobWizardCommon.assertValidationCallouts(['mlValidationCallout warning']); + await ml.jobWizardCommon.assertCalloutText( + 'mlValidationCallout warning', + /Job will not be able to run in the current cluster because model memory limit is higher than/ + ); + }); }); } diff --git a/x-pack/test/functional/services/ml/common_ui.ts b/x-pack/test/functional/services/ml/common_ui.ts index ef2605a716dbf..282ae1aba5033 100644 --- a/x-pack/test/functional/services/ml/common_ui.ts +++ b/x-pack/test/functional/services/ml/common_ui.ts @@ -447,5 +447,9 @@ export function MachineLearningCommonUIProvider({ await testSubjects.missingOrFail(selector); } }, + + async toggleSwitchIfNeeded(testSubj: string, targetState: boolean) { + await testSubjects.setEuiSwitch(testSubj, targetState ? 'check' : 'uncheck'); + }, }; } diff --git a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts index 50d7738abf732..8e0c1e84b4f5d 100644 --- a/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts +++ b/x-pack/test/functional/services/ml/data_frame_analytics_creation.ts @@ -553,12 +553,12 @@ export function MachineLearningDataFrameAnalyticsCreationProvider( async assertValidationCalloutsExists() { await retry.tryForTime(4000, async () => { - await testSubjects.existOrFail('mlValidationCallout'); + await testSubjects.existOrFail('~mlValidationCallout'); }); }, async assertAllValidationCalloutsPresent(expectedNumCallouts: number) { - const validationCallouts = await testSubjects.findAll('mlValidationCallout'); + const validationCallouts = await testSubjects.findAll('~mlValidationCallout'); expect(validationCallouts.length).to.eql(expectedNumCallouts); }, diff --git a/x-pack/test/functional/services/ml/job_wizard_common.ts b/x-pack/test/functional/services/ml/job_wizard_common.ts index e1e5b4ea0d45b..b1671626f191f 100644 --- a/x-pack/test/functional/services/ml/job_wizard_common.ts +++ b/x-pack/test/functional/services/ml/job_wizard_common.ts @@ -26,6 +26,7 @@ export function MachineLearningJobWizardCommonProvider( const retry = getService('retry'); const testSubjects = getService('testSubjects'); const headerPage = getPageObject('header'); + const browser = getService('browser'); function advancedSectionSelector(subSelector?: string) { const subj = 'mlJobWizardAdvancedSection'; @@ -628,5 +629,78 @@ export function MachineLearningJobWizardCommonProvider( await testSubjects.existOrFail(expectedSelector); }); }, + + async assertAnnotationRecommendationCalloutVisible(expectVisible: boolean = true) { + const callOutTestSubj = 'mlJobWizardAlsoEnableAnnotationsRecommendationCallout'; + if (expectVisible) + await testSubjects.existOrFail(callOutTestSubj, { + timeout: 3_000, + }); + else + await testSubjects.missingOrFail(callOutTestSubj, { + timeout: 3_000, + }); + }, + + async goToTimeRangeStep() { + await retry.tryForTime(60_000, async () => { + await testSubjects.existOrFail('mlJobWizardTimeRangeStep'); + await testSubjects.click('mlJobWizardTimeRangeStep'); + await this.assertTimeRangeSectionExists(); + }); + }, + + async goToValidationStep() { + await retry.tryForTime(60_000, async () => { + await testSubjects.existOrFail('mlJobWizardValidationStep'); + await testSubjects.click('mlJobWizardValidationStep'); + await this.assertValidationSectionExists(); + }); + }, + + async setTimeRange({ startTime, endTime }: { startTime?: string; endTime?: string }) { + const opts = { + clearWithKeyboard: true, + typeCharByChar: true, + }; + + if (startTime) + await testSubjects.setValue('mlJobWizardDatePickerRangeStartDate', startTime, opts); + if (endTime) await testSubjects.setValue('mlJobWizardDatePickerRangeEndDate', endTime, opts); + + // escape popover + await browser.pressKeys(browser.keys.ESCAPE); + }, + + async goToJobDetailsStep() { + await testSubjects.existOrFail('mlJobWizardJobDetailsStep', { + timeout: 3_000, + }); + await testSubjects.click('mlJobWizardJobDetailsStep'); + await this.assertJobDetailsSectionExists(); + }, + + async assertValidationCallouts(expectedCallOutSelectors: string[]) { + for await (const sel of expectedCallOutSelectors) + await testSubjects.existOrFail(sel, { + timeout: 3_000, + }); + }, + + async assertCalloutText(calloutStatusTestSubj: string, expectedText: RegExp) { + const allCalloutStatusTexts = await testSubjects.getVisibleTextAll(calloutStatusTestSubj); + + const oneCalloutMatches = allCalloutStatusTexts.some( + (visibleText) => !!visibleText.match(expectedText) + ); + expect(oneCalloutMatches).to.eql( + true, + `Expect one of the callouts [${calloutStatusTestSubj}] to match [${expectedText}], instead found ${JSON.stringify( + allCalloutStatusTexts, + null, + 2 + )}` + ); + }, }; } From efd48871c58a71570ae8de37130ee45cd79b9129 Mon Sep 17 00:00:00 2001 From: Konrad Szwarc Date: Mon, 27 May 2024 14:25:00 +0200 Subject: [PATCH 12/59] [EDR Workflows][Osquery] Use newly added action responses data stream (#184209) Follow up to https://github.com/elastic/kibana/pull/183892 with a commit that got lost during local rebase. --- .../factory/actions/results/query.action_results.dsl.ts | 6 +++--- .../plugins/osquery/server/search_strategy/osquery/index.ts | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts index 2ed9d406895a5..18a9c4fab8332 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/factory/actions/results/query.action_results.dsl.ts @@ -47,11 +47,11 @@ export const buildActionResultsQuery = ({ let index: string; if (useNewDataStream) { - index = ACTION_RESPONSES_DATA_STREAM_INDEX; + index = `${ACTION_RESPONSES_DATA_STREAM_INDEX}*`; } else if (componentTemplateExists) { - index = ACTION_RESPONSES_INDEX; + index = `${ACTION_RESPONSES_INDEX}*`; } else { - index = AGENT_ACTIONS_RESULTS_INDEX; + index = `${AGENT_ACTIONS_RESULTS_INDEX}*`; } return { diff --git a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts index 3cc3f8ad6b6c1..efc9dd6582ced 100644 --- a/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts +++ b/x-pack/plugins/osquery/server/search_strategy/osquery/index.ts @@ -40,6 +40,8 @@ export const osquerySearchStrategyProvider = ( }), newDataStreamIndexExists: esClient.asInternalUser.indices.exists({ index: `${ACTION_RESPONSES_DATA_STREAM_INDEX}*`, + allow_no_indices: false, + expand_wildcards: 'all', }), }).pipe( mergeMap(({ actionsIndexExists, newDataStreamIndexExists }) => { From ab78b725ba94be4f05f564df4e2a4b103d3d1b33 Mon Sep 17 00:00:00 2001 From: Alex Szabo Date: Mon, 27 May 2024 15:30:12 +0200 Subject: [PATCH 13/59] [CI] Serverless Release assistant to consider recent QAF builds (#184306) ## Summary Builds on: https://github.com/elastic/qaf-tests/pull/75 If we only rely on the 3-hour cycle of the QAF test suite as an indication for a "releaseable" build, then we always work with this 3-4 hour lead time. This can make releasing on-demand very delayed. @pheyos added `PRE_RELEASE_CHECK=true` to some builds, we can use this as an indicator that a test run was meant to prove the releaseability of a version. This PR also moves the release assistant job together with other ES_Serverless jobs. --- .../serverless/create_deploy_tag/info_sections/build_info.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts index 0315d59ec9ca4..cd459b1debaab 100644 --- a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts +++ b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts @@ -79,8 +79,8 @@ export async function getQAFBuildContainingCommit( // Find the first build that contains this commit const build = qafBuilds - // Only search across scheduled builds, triggered builds might run with different commits - .filter((e) => e.source === 'schedule') + // Scheduled and manually tagged builds will have this variable, other builds might have non-relevant commits + .filter((e) => e.env.PRE_RELEASE_CHECK?.match(/(1|true)/i)) .find((kbBuild) => { const commitShaIndex = recentGitCommits.findIndex((c) => c.sha === commitSha); From 14a8a09fcd2b4eec20925bc92addaf924126df68 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Mon, 27 May 2024 15:30:38 +0200 Subject: [PATCH 14/59] avoid calculating hash of all index pattern properties (#184292) --- .../common/calculate_object_hash.test.ts | 24 +++++++++++++++++++ .../common/calculate_object_hash.ts | 3 +++ 2 files changed, 27 insertions(+) create mode 100644 src/plugins/kibana_utils/common/calculate_object_hash.test.ts diff --git a/src/plugins/kibana_utils/common/calculate_object_hash.test.ts b/src/plugins/kibana_utils/common/calculate_object_hash.test.ts new file mode 100644 index 0000000000000..3b11210b2e566 --- /dev/null +++ b/src/plugins/kibana_utils/common/calculate_object_hash.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { calculateObjectHash } from './calculate_object_hash'; + +describe('calculateObjectHash', () => { + test('calculates hash of the object', () => { + const object = { test: 123 }; + + expect(calculateObjectHash(object)).toEqual('5094c3dc'); + }); + + test('ignore inner props of index object expect for the value.id', () => { + const object1 = { test: 123, index: { value: { id: 'test', otherprop: 1 } } }; + const object2 = { test: 123, index: { value: { id: 'test', otherprop: 2 } } }; + + expect(calculateObjectHash(object1)).toEqual(calculateObjectHash(object2)); + }); +}); diff --git a/src/plugins/kibana_utils/common/calculate_object_hash.ts b/src/plugins/kibana_utils/common/calculate_object_hash.ts index dcd24b1f9f074..9cf7266b1521d 100644 --- a/src/plugins/kibana_utils/common/calculate_object_hash.ts +++ b/src/plugins/kibana_utils/common/calculate_object_hash.ts @@ -53,6 +53,9 @@ function foldValue(input: number, value: any, key: string, seen: any[]) { if (key === 'vis' && value.constructor.name === 'Vis') { return hash; } + if (key === 'index' && value.value?.id) { + return fold(hash, value.value.id); + } if (seen.indexOf(value) !== -1) { return fold(hash, '[Circular]' + key); } From a392e59fdf9addcebaaae185ca80d072e8235693 Mon Sep 17 00:00:00 2001 From: parthpuri-elastic <150776158+parthpuri-elastic@users.noreply.github.com> Date: Mon, 27 May 2024 19:54:18 +0530 Subject: [PATCH 15/59] [Search] Add DLS parameters in native Outlook connector (#184098) ## Summary Related to https://github.com/elastic/connectors/issues/2405 Add new config parameter in Outlook Connector related to Document Level Security ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) Version: 8.15.0 --- .../types/native_connectors.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/kbn-search-connectors/types/native_connectors.ts b/packages/kbn-search-connectors/types/native_connectors.ts index 422e31d2282be..e8976b0f09f7b 100644 --- a/packages/kbn-search-connectors/types/native_connectors.ts +++ b/packages/kbn-search-connectors/types/native_connectors.ts @@ -3583,12 +3583,34 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record Date: Mon, 27 May 2024 16:56:52 +0200 Subject: [PATCH 16/59] [Search] Add sync status to connector page (#184024) ## Summary Adds Sync status to connector page. Adds a marker for content/access control sync buttons when last sync failed. Changes logic to show error message to make sure access control errors aren't lost. Fixes incomplete connector query to show correct counts on list. Screenshot 2024-05-22 at 16 01 27 Screenshot 2024-05-22 at 16 03 12 ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [x] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [x] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [x] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../connector_detail/connector_stats.tsx | 6 +-- .../connector_detail/connector_view_logic.ts | 6 ++- .../components/connector_detail/overview.tsx | 5 ++- .../components/connectors/connector_stats.tsx | 1 + .../connectors/connectors_table.tsx | 8 +--- .../search_index/index_view_logic.ts | 6 ++- .../components/search_index/overview.tsx | 18 +++++++-- .../search_index/sync_jobs/sync_jobs.tsx | 12 +++++- .../utils/connector_status_helpers.ts | 37 +++++++++++++------ .../server/utils/get_sync_jobs_queries.ts | 24 +++++------- 10 files changed, 79 insertions(+), 44 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx index 095844b4e04d4..d8725141eb5e3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_stats.tsx @@ -122,10 +122,8 @@ export const ConnectorStats: React.FC = ({ connector, index - - {connectorStatusToText(connector?.status, !!connector?.index_name)} + + {connectorStatusToText(connector)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts index f7d6f30ee7d54..42d881300ac5d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/connector_view_logic.ts @@ -158,7 +158,11 @@ export const ConnectorViewLogic = kea [selectors.connector], (connector) => connector?.id], error: [ () => [selectors.connector], - (connector: Connector | undefined) => connector?.error || connector?.last_sync_error || null, + (connector: Connector | undefined) => + connector?.error || + connector?.last_sync_error || + connector?.last_access_control_sync_error || + null, ], indexName: [ () => [selectors.connector], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx index a97f7b8c862fd..1931f0f945505 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connector_detail/overview.tsx @@ -186,7 +186,10 @@ export const ConnectorDetailOverview: React.FC = () => { {connector && connector.service_type !== ENTERPRISE_SEARCH_CONNECTOR_CRAWLER_SERVICE_TYPE && ( <> - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx index 8b2ad4490a1a6..db76e97ec719e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connector_stats.tsx @@ -213,6 +213,7 @@ export const ConnectorStats: React.FC = ({ isCrawler }) => {}} onClickAriaLabel={getSyncJobErrorsLabel(errorCount, isCrawler)} + color={errorCount > 0 ? 'danger' : 'default'} > {getSyncJobErrorsLabel(errorCount, isCrawler)} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx index daf3f59eef45e..82f258487f39e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/connectors/connectors_table.tsx @@ -135,12 +135,8 @@ export const ConnectorsTable: React.FC = ({ } ), render: (connector: ConnectorViewItem) => { - const label = connectorStatusToText(connector.status, !!connector.index_name); - return ( - - {label} - - ); + const label = connectorStatusToText(connector); + return {label}; }, truncateText: true, width: '15%', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts index 57f6d8f11bfce..597757e83534a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/index_view_logic.ts @@ -264,7 +264,11 @@ export const IndexViewLogic = kea [selectors.connector], - (connector: Connector | undefined) => connector?.error || connector?.last_sync_error || null, + (connector: Connector | undefined) => + connector?.error || + connector?.last_sync_error || + connector?.last_access_control_sync_error || + null, ], hasAdvancedFilteringFeature: [ () => [selectors.connector], diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx index b280d3fbf36b7..f8ccf71026e83 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/overview.tsx @@ -81,7 +81,11 @@ export const SearchIndexOverview: React.FC = () => { defaultMessage="Convert it to a {link}, to be self-managed on your own infrastructure. Native connectors are available only in your Elastic Cloud deployment." values={{ link: ( - + {i18n.translate( 'xpack.enterpriseSearch.content.searchIndex.nativeCloudCallout.connectorClient', { defaultMessage: 'connector client' } @@ -93,7 +97,12 @@ export const SearchIndexOverview: React.FC = () => {

- showModal()}> + showModal()} + > {i18n.translate( 'xpack.enterpriseSearch.content.indices.searchIndex.convertConnector.buttonLabel', { defaultMessage: 'Convert connector' } @@ -126,7 +135,10 @@ export const SearchIndexOverview: React.FC = () => { {isConnectorIndex(indexData) && ( <> - + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx index 2013d601df4aa..49f4959100736 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs.tsx @@ -21,7 +21,15 @@ import { IndexViewLogic } from '../index_view_logic'; import { SyncJobsViewLogic } from './sync_jobs_view_logic'; -export const SyncJobs: React.FC = () => { +export interface SyncJobsProps { + errorOnAccessSync?: boolean; + errorOnContentSync?: boolean; +} + +export const SyncJobs: React.FC = ({ + errorOnAccessSync = false, + errorOnContentSync = false, +}) => { const { hasDocumentLevelSecurityFeature } = useValues(IndexViewLogic); const { productFeatures } = useValues(KibanaLogic); const shouldShowAccessSyncs = @@ -74,6 +82,7 @@ export const SyncJobs: React.FC = () => { 'xpack.enterpriseSearch.content.syncJobs.lastSync.tableSelector.content.label', { defaultMessage: 'Content syncs' } ), + ...(errorOnContentSync ? { iconSide: 'right', iconType: 'warning' } : {}), }, { @@ -82,6 +91,7 @@ export const SyncJobs: React.FC = () => { 'xpack.enterpriseSearch.content.syncJobs.lastSync.tableSelector.accessControl.label', { defaultMessage: 'Access control syncs' } ), + ...(errorOnAccessSync ? { iconSide: 'right', iconType: 'warning' } : {}), }, ]} /> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts index ef8e84177eae1..587539498c786 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/connector_status_helpers.ts @@ -6,17 +6,16 @@ */ import { i18n } from '@kbn/i18n'; -import { ConnectorStatus } from '@kbn/search-connectors'; +import { Connector, ConnectorStatus, SyncStatus } from '@kbn/search-connectors'; const incompleteText = i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.ingestionStatus.incomplete.label', { defaultMessage: 'Incomplete' } ); -export function connectorStatusToText( - connectorStatus: ConnectorStatus, - hasIndexName: boolean -): string { +export function connectorStatusToText(connector: Connector): string { + const hasIndexName = !!connector.index_name; + const connectorStatus = connector.status; if ( connectorStatus === ConnectorStatus.CREATED || connectorStatus === ConnectorStatus.NEEDS_CONFIGURATION @@ -26,6 +25,16 @@ export function connectorStatusToText( { defaultMessage: 'Needs Configuration' } ); } + if ( + connector.error === SyncStatus.ERROR || + connector.last_sync_error !== null || + connector.last_access_control_sync_error !== null + ) { + return i18n.translate( + 'xpack.enterpriseSearch.content.searchIndices.connectorStatus.syncFailure.label', + { defaultMessage: 'Sync Failure' } + ); + } if (connectorStatus === ConnectorStatus.ERROR) { return i18n.translate( 'xpack.enterpriseSearch.content.searchIndices.connectorStatus.connectorFailure.label', @@ -51,18 +60,22 @@ export function connectorStatusToText( return incompleteText; } -export function connectorStatusToColor( - connectorStatus: ConnectorStatus, - hasIndexName: boolean -): 'warning' | 'danger' | 'success' { +export function connectorStatusToColor(connector: Connector): 'warning' | 'danger' | 'success' { + const hasIndexName = !!connector.index_name; + const connectorStatus = connector.status; if (!hasIndexName) { return 'warning'; } + if ( + connectorStatus === ConnectorStatus.ERROR || + connector.error === SyncStatus.ERROR || + connector.last_sync_error !== null || + connector.last_access_control_sync_error !== null + ) { + return 'danger'; + } if (connectorStatus === ConnectorStatus.CONNECTED) { return 'success'; } - if (connectorStatus === ConnectorStatus.ERROR) { - return 'danger'; - } return 'warning'; } diff --git a/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts b/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts index 5fe2e9b17eff7..ff6eeee4db472 100644 --- a/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts +++ b/x-pack/plugins/enterprise_search/server/utils/get_sync_jobs_queries.ts @@ -331,24 +331,18 @@ export const getIncompleteCountQuery = (isCrawler?: boolean) => { } return { bool: { - should: [ - { - bool: { - must_not: { - terms: { - status: [ConnectorStatus.CONNECTED, ConnectorStatus.ERROR], - }, - }, - }, + must_not: { + terms: { + status: [ConnectorStatus.CONNECTED, ConnectorStatus.ERROR], }, - { - range: { - last_seen: { - lt: moment().subtract(30, 'minutes').toISOString(), - }, + }, + must: { + range: { + last_seen: { + lt: moment().subtract(30, 'minutes').toISOString(), }, }, - ], + }, filter: [ { bool: { From 099e79a064507780d3d4d7c983ded125caa26ed5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 27 May 2024 18:05:13 +0200 Subject: [PATCH 17/59] [config-schema] Add docs for new `lazy` type (#184124) ## Summary Per the title. Related https://github.com/elastic/kibana/pull/184000 --- packages/kbn-config-schema/README.md | 111 ++++++++++++------ .../kbn-config-schema/src/types/lazy.test.ts | 24 ++++ 2 files changed, 101 insertions(+), 34 deletions(-) diff --git a/packages/kbn-config-schema/README.md b/packages/kbn-config-schema/README.md index a4f2c1f6458cf..5916f49ef2836 100644 --- a/packages/kbn-config-schema/README.md +++ b/packages/kbn-config-schema/README.md @@ -5,35 +5,38 @@ Kibana configuration entries providing developers with a fully typed model of th ## Table of Contents -- [Why `@kbn/config-schema`?](#why-kbnconfig-schema) -- [Schema building blocks](#schema-building-blocks) - - [Basic types](#basic-types) - - [`schema.string()`](#schemastring) - - [`schema.number()`](#schemanumber) - - [`schema.boolean()`](#schemaboolean) - - [`schema.literal()`](#schemaliteral) - - [`schema.buffer()`](#schemabuffer) - - [`schema.stream()`](#schemastream) - - [Composite types](#composite-types) - - [`schema.arrayOf()`](#schemaarrayof) - - [`schema.object()`](#schemaobject) - - [`schema.recordOf()`](#schemarecordof) - - [`schema.mapOf()`](#schemamapof) - - [Advanced types](#advanced-types) - - [`schema.oneOf()`](#schemaoneof) - - [`schema.any()`](#schemaany) - - [`schema.maybe()`](#schemamaybe) - - [`schema.nullable()`](#schemanullable) - - [`schema.never()`](#schemanever) - - [`schema.uri()`](#schemauri) - - [`schema.byteSize()`](#schemabytesize) - - [`schema.duration()`](#schemaduration) - - [`schema.conditional()`](#schemaconditional) - - [References](#references) - - [`schema.contextRef()`](#schemacontextref) - - [`schema.siblingRef()`](#schemasiblingref) -- [Custom validation](#custom-validation) -- [Default values](#default-values) +- [`@kbn/config-schema` — The Kibana config validation library](#kbnconfig-schema--the-kibana-config-validation-library) + - [Table of Contents](#table-of-contents) + - [Why `@kbn/config-schema`?](#why-kbnconfig-schema) + - [Schema building blocks](#schema-building-blocks) + - [Basic types](#basic-types) + - [`schema.string()`](#schemastring) + - [`schema.number()`](#schemanumber) + - [`schema.boolean()`](#schemaboolean) + - [`schema.literal()`](#schemaliteral) + - [`schema.buffer()`](#schemabuffer) + - [`schema.stream()`](#schemastream) + - [Composite types](#composite-types) + - [`schema.arrayOf()`](#schemaarrayof) + - [`schema.object()`](#schemaobject) + - [`schema.recordOf()`](#schemarecordof) + - [`schema.mapOf()`](#schemamapof) + - [Advanced types](#advanced-types) + - [`schema.oneOf()`](#schemaoneof) + - [`schema.any()`](#schemaany) + - [`schema.maybe()`](#schemamaybe) + - [`schema.nullable()`](#schemanullable) + - [`schema.never()`](#schemanever) + - [`schema.uri()`](#schemauri) + - [`schema.byteSize()`](#schemabytesize) + - [`schema.duration()`](#schemaduration) + - [`schema.conditional()`](#schemaconditional) + - [`schema.lazy()`](#schemalazy) + - [References](#references) + - [`schema.contextRef()`](#schemacontextref) + - [`schema.siblingRef()`](#schemasiblingref) + - [Custom validation](#custom-validation) + - [Default values](#default-values) ## Why `@kbn/config-schema`? @@ -44,7 +47,7 @@ There are a number of reasons why we decided to roll our own solution for the co * **Limited API surface** - having a future rich library is awesome, but it's a really hard task to audit such library and make sure everything is sane and secure enough. As everyone knows complexity is the enemy of security and hence we'd like to have a full control over what exactly we expose and commit to maintain. * **Custom error messages** - detailed validation error messages are a great help to developers, but at the same time they can contain information that's way too sensitive to expose to everyone. We'd like to control these messages and make them only as detailed as really needed. For example, we don't want validation error messages to contain the passwords for internal users to show-up in the logs. These logs are commonly ingested into Elasticsearch, and accessible to a large number of users which shouldn't have access to the internal user's password. * **Type information** - having run-time guarantees is great, but additionally having compile-time guarantees is even better. We'd like to provide developers with a fully typed model of the validated data so that it's harder to misuse it _after_ validation. -* **Upgradability** - no matter how well a validation library is implemented, it will have bugs and may need to be improved at some point anyway. Some external libraries are very well supported, some aren't or won't be in the future. It's always a risk to depend on an external party with their own release cadence when you need to quickly fix a security vulnerability in a patch version. We'd like to have a better control over lifecycle of such an important piece of our codebase. +* **Upgradability** - no matter how well a validation library is implemented, it will have bugs and may need to be improved at some point anyway. Some external libraries are very well supported, some aren't or won't be in the future. It's always a risk to depend on an external party with their own release cadence when you need to quickly fix a security vulnerability in a patch version. We'd like to have a better control over lifecycle of such an important piece of our codebase. ## Schema building blocks @@ -243,7 +246,7 @@ __Options:__ __Usage:__ ```typescript -const valueSchema = schema.object({ +const valueSchema = schema.object({ isEnabled: schema.boolean({ defaultValue: false }), name: schema.string({ minLength: 10 }), }); @@ -461,6 +464,46 @@ const valueSchema = schema.object({ __Notes:__ * Conditional schemas may be hard to read and understand and hence should be used only sparingly. +#### `schema.lazy()` + +Allows recursive runtime types to be defined. + +Takes a required generic type argument and a required string that represents the id of the schema. + +It is recommended to pick a globally unique ID for your schema. Consider creating only IDs that are prefixed with your +domain, e.g. `myPlugin_myRecursiveType`. + +IDs must be unique within a _schema ancestry_. You can use the same ID in multiple, separate schemas as long as they do not +share a common ancestor object. However, if you want to generate OAS from your schema you must ensure a globally +unique ID in order to avoid overriding schemas with the same ID. + +Note: use of `meta.id` is required to associate the schema with the ID used in the `schema.lazy()` call in order to +create a recursive type (see usage). + +__Output type:__ `T` + +__Usage:__ +```typescript +interface RecursiveType { + name: string; + self: undefined | RecursiveType; +} + +// Do not assign this ID to any other schema to avoid collisions. +const id = 'myPlugin_myRecursiveType'; +const object = schema.object( + { + name: schema.string(), + self: schema.lazy(id), + }, + { meta: { id } } +); +``` + +__Notes:__ +* Preferably use this sparingly and only to create recursive types. +* Intended to be used only as properties within `schema.object()` types. + ### References #### `schema.contextRef()` @@ -471,7 +514,7 @@ __Output type:__ `TReferenceValue` __Usage:__ ```typescript -const valueSchema = schema.object({ +const valueSchema = schema.object({ env: schema.string({ defaultValue: schema.contextRef('envName') }), }); valueSchema.validate({}, { envName: 'dev' }); @@ -479,7 +522,7 @@ valueSchema.validate({}, { envName: 'dev' }); __Notes:__ * The `@kbn/config-schema` neither validates nor coerces the "dereferenced" value and the developer is responsible for making sure that it has the appropriate type. -* The root context that Kibana provides during config validation includes lots of useful properties like `environment name` that can be used to provide a strict schema for production and more relaxed one for development. +* The root context that Kibana provides during config validation includes lots of useful properties like `environment name` that can be used to provide a strict schema for production and more relaxed one for development. #### `schema.siblingRef()` @@ -529,7 +572,7 @@ to denote the failed validation or not return anything at all (`void`) otherwise Another use case for custom validation functions is when the schema depends on some run-time data: ```typescript -const gesSchema = randomRunTimeSeed => schema.string({ +const gesSchema = randomRunTimeSeed => schema.string({ validate: value => value !== randomRunTimeSeed ? 'value is not allowed' : undefined }); diff --git a/packages/kbn-config-schema/src/types/lazy.test.ts b/packages/kbn-config-schema/src/types/lazy.test.ts index 52864a9c825a8..6ff18fd730bdb 100644 --- a/packages/kbn-config-schema/src/types/lazy.test.ts +++ b/packages/kbn-config-schema/src/types/lazy.test.ts @@ -68,4 +68,28 @@ describe('lazy', () => { expect(value).toEqual(invalidSelf); expect(error?.message).toBe('expected value of type [string] but got [number]'); }); + + it('requires a schema with a given ID to be present in the schema when validating', () => { + expect(() => + schema + .object({ + lazy: schema.lazy('unknown'), + }) + .validate({ lazy: {} }) + ).toThrow(/outside of schema boundaries/); + }); + + it('disallows duplicate ids in the same schema', () => { + const dupId = 'dupId'; + const schema1 = schema.object({ a: schema.string() }, { meta: { id: dupId } }); + const schema2 = schema.object({ b: schema.string() }, { meta: { id: dupId } }); + + expect(() => + schema.object({ + schema1, + schema2, + lazy: schema.lazy(dupId), + }) + ).toThrow(/Cannot add different schemas with the same id/); + }); }); From a79edf109839fe4de13a4446173a5820f9b05751 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 27 May 2024 10:15:51 -0700 Subject: [PATCH 18/59] [HTTP/OAS] Add descriptions for short URL APIs (#184267) --- .../url_service/http/short_urls/register_create_route.ts | 4 ++++ .../url_service/http/short_urls/register_delete_route.ts | 4 ++++ .../server/url_service/http/short_urls/register_get_route.ts | 4 ++++ .../url_service/http/short_urls/register_resolve_route.ts | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts index 97594837f0720..0848fdfbfe605 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_create_route.ts @@ -15,6 +15,10 @@ export const registerCreateRoute = (router: IRouter, url: ServerUrlService) => { router.post( { path: '/api/short_url', + options: { + access: 'public', + description: `Create a short URL`, + }, validate: { body: schema.object({ locatorId: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts index ddc29117a3acc..258faff0d04a6 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_delete_route.ts @@ -14,6 +14,10 @@ export const registerDeleteRoute = (router: IRouter, url: ServerUrlService) => { router.delete( { path: '/api/short_url/{id}', + options: { + access: 'public', + description: `Delete a short URL`, + }, validate: { params: schema.object({ id: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts index 8e783b3fcfd3d..ae108ccd11d97 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_get_route.ts @@ -14,6 +14,10 @@ export const registerGetRoute = (router: IRouter, url: ServerUrlService) => { router.get( { path: '/api/short_url/{id}', + options: { + access: 'public', + description: `Get a short URL`, + }, validate: { params: schema.object({ id: schema.string({ diff --git a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts index 47290d5754545..6076889945f36 100644 --- a/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts +++ b/src/plugins/share/server/url_service/http/short_urls/register_resolve_route.ts @@ -15,6 +15,10 @@ export const registerResolveRoute = (router: IRouter, url: ServerUrlService) => router.get( { path: '/api/short_url/_slug/{slug}', + options: { + access: 'public', + description: `Resolve a short URL`, + }, validate: { params: schema.object({ slug: schema.string({ From b162896e976f7f1c8d7d384346556d0ab92ed722 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 27 May 2024 10:18:01 -0700 Subject: [PATCH 19/59] [HTTP/OAS] Add description for features API (#184248) --- x-pack/plugins/features/server/routes/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/features/server/routes/index.ts b/x-pack/plugins/features/server/routes/index.ts index bb78f07bc56cc..621bf4a4b0e87 100644 --- a/x-pack/plugins/features/server/routes/index.ts +++ b/x-pack/plugins/features/server/routes/index.ts @@ -21,7 +21,11 @@ export function defineRoutes({ router, featureRegistry }: RouteDefinitionParams) router.get( { path: '/api/features', - options: { tags: ['access:features'] }, + options: { + tags: ['access:features'], + access: 'public', + description: `Get features`, + }, validate: { query: schema.object({ ignoreValidLicenses: schema.boolean({ defaultValue: false }) }), }, From a5269144dd5d0c69e80e66d0341472f15ee0eac0 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Mon, 27 May 2024 21:05:52 +0200 Subject: [PATCH 20/59] [Advanced settings] Hide `Defer loading panels below "the fold"` setting (#184090) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/elastic/kibana/issues/183166 ## Summary This small PR hides the previously mentioned setting in serverless and adds a uiOverride to keep it always disabled too. ##### How to test * Verify that the setting is disabled and hidden for serverless * Verify that the setting is still functional for onprem --------- Co-authored-by: Yulia Čech <6585477+yuliacech@users.noreply.github.com> --- config/serverless.yml | 4 +++- packages/serverless/settings/common/index.ts | 3 --- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/config/serverless.yml b/config/serverless.yml index 055f564539c2e..654b2a0a3a55e 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -137,10 +137,12 @@ xpack.alerting.rules.maxScheduledPerMinute: 400 xpack.actions.run.maxAttempts: 10 xpack.actions.queued.max: 10000 -# Disables ESQL in advanced settings (hides it from the UI) uiSettings: overrides: + # Disables ESQL in advanced settings (hides it from the UI) enableESQL: true + # Disables `Defer loading panels below "the fold"` + labs:dashboard:deferBelowFold: false # Task Manager xpack.task_manager.allow_reading_invalid_state: false diff --git a/packages/serverless/settings/common/index.ts b/packages/serverless/settings/common/index.ts index f8f608bdf64f5..09d6b696da7f5 100644 --- a/packages/serverless/settings/common/index.ts +++ b/packages/serverless/settings/common/index.ts @@ -26,8 +26,6 @@ const GENERAL_SETTINGS = [ settings.TIMEPICKER_TIME_DEFAULTS_ID, ]; -const PRESENTATION_LABS_SETTINGS = [settings.LABS_DASHBOARD_DEFER_BELOW_FOLD_ID]; - const ACCESSIBILITY_SETTINGS = [settings.ACCESSIBILITY_DISABLE_ANIMATIONS_ID]; const BANNER_SETTINGS = [ @@ -49,7 +47,6 @@ const NOTIFICATION_SETTINGS = [ export const ALL_COMMON_SETTINGS = [ ...GENERAL_SETTINGS, - ...PRESENTATION_LABS_SETTINGS, ...ACCESSIBILITY_SETTINGS, ...BANNER_SETTINGS, ...DISCOVER_SETTINGS, From 193668c7903a966384a42f006300d6d8f056464d Mon Sep 17 00:00:00 2001 From: Panagiota Mitsopoulou Date: Mon, 27 May 2024 21:39:50 +0200 Subject: [PATCH 21/59] [SLO Embeddable rebuild] migrate slo alerts embeddable (#182094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes https://github.com/elastic/kibana/issues/179295 ## 🍒 Summary This PR converts the SLO Alerts embeddable to the new React Embeddable framework. There are no UI changes to the current embeddable and the behavior should be the same. I have a video of how the embeddable should work https://github.com/elastic/kibana/assets/2852703/9edd9afe-9a94-4d1d-b9ec-fd43b37788a9 ## ✔️ Acceptance criteria - A new `create_alerts_panel_action` is created, which adds the `SLO Alerts` option in the Add panel section - The `SLO Alerts` menu option is grouped under SLOs menu - `SLOs included` link opens the SLO configuration, which also appears under `More` panel options - Kibana screenshot tool should report no timeout error. - Clicking on the Refresh button should reload the embeddable - The embeddable should react to the timerange changes --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../presentation_publishing/index.ts | 2 +- .../interfaces/fetch/fetch.ts | 19 ++ .../alerts/components/slo_alerts_summary.tsx | 20 +-- .../alerts/components/slo_alerts_table.tsx | 3 +- .../slo/alerts/{index.ts => constants.ts} | 4 +- .../slo/alerts/slo_alerts_embeddable.tsx | 165 ------------------ .../alerts/slo_alerts_embeddable_factory.ts | 71 -------- .../alerts/slo_alerts_embeddable_factory.tsx | 133 ++++++++++++++ ....tsx => slo_alerts_open_configuration.tsx} | 20 +-- .../slo/alerts/slo_alerts_wrapper.tsx | 28 +-- .../slo/alerts/slo_configuration.tsx | 4 +- .../slo/public/embeddable/slo/alerts/types.ts | 56 +++++- .../slo/public/plugin.ts | 26 +-- .../ui_actions/create_alerts_panel_action.tsx | 56 ++++++ .../ui_actions/edit_slo_alerts_panel.tsx | 31 ++-- .../slo/public/ui_actions/index.ts | 3 + 16 files changed, 323 insertions(+), 318 deletions(-) rename x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/{index.ts => constants.ts} (61%) delete mode 100644 x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable.tsx delete mode 100644 x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts create mode 100644 x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx rename x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/{handle_explicit_input.tsx => slo_alerts_open_configuration.tsx} (82%) create mode 100644 x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx diff --git a/packages/presentation/presentation_publishing/index.ts b/packages/presentation/presentation_publishing/index.ts index d1c3132e86c9f..975f6f3863de1 100644 --- a/packages/presentation/presentation_publishing/index.ts +++ b/packages/presentation/presentation_publishing/index.ts @@ -29,7 +29,7 @@ export { useInheritedViewMode, type CanAccessViewMode, } from './interfaces/can_access_view_mode'; -export { fetch$, type FetchContext } from './interfaces/fetch/fetch'; +export { fetch$, useFetchContext, type FetchContext } from './interfaces/fetch/fetch'; export { initializeTimeRange, type SerializedTimeRange, diff --git a/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts b/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts index 8b77e366219cb..466fd3a602a8c 100644 --- a/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts +++ b/packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts @@ -7,6 +7,7 @@ */ import { + BehaviorSubject, combineLatest, debounceTime, delay, @@ -23,6 +24,7 @@ import { tap, } from 'rxjs'; import { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; +import { useMemo, useEffect } from 'react'; import { apiPublishesTimeRange, apiPublishesUnifiedSearch, @@ -32,6 +34,7 @@ import { import { apiPublishesSearchSession, PublishesSearchSession } from './publishes_search_session'; import { apiHasParentApi, HasParentApi } from '../has_parent_api'; import { apiPublishesReload } from './publishes_reload'; +import { useStateFromPublishingSubject } from '../../publishing_subject'; export interface FetchContext { isReload: boolean; @@ -145,3 +148,19 @@ export function fetch$(api: unknown): Observable { return merge(immediateChange$, batchedChanges$).pipe(startWith(getFetchContext(api, false))); } + +export const useFetchContext = (api: unknown): FetchContext => { + const context$: BehaviorSubject = useMemo(() => { + return new BehaviorSubject(getFetchContext(api, false)); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + const subsctription = fetch$(api).subscribe((nextContext) => context$.next(nextContext)); + + return () => subsctription.unsubscribe(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return useStateFromPublishingSubject(context$); +}; diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx index da41a5938baa0..968d1f80a0824 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_summary.tsx @@ -12,7 +12,7 @@ import { calculateTimeRangeBucketSize } from '@kbn/observability-plugin/public'; import { observabilityAlertFeatureIds } from '@kbn/observability-plugin/common'; import { useSloAlertsQuery } from './slo_alerts_table'; -import { SloEmbeddableDeps } from '../slo_alerts_embeddable'; +import { SloEmbeddableDeps } from '../types'; import { SloItem } from '../types'; const DEFAULT_INTERVAL = '60s'; @@ -50,17 +50,13 @@ export function SloAlertsSummary({ ), [timeRange.from, timeRange.to, timeBuckets] ); - const alertSummaryTimeRange = useMemo( - () => - getAlertSummaryTimeRange( - { - from: timeRange.from, - to: timeRange.to, - }, - bucketSize?.intervalString ?? DEFAULT_INTERVAL, - bucketSize?.dateFormat ?? DEFAULT_DATE_FORMAT - ), - [timeRange.from, timeRange.to, bucketSize] + const alertSummaryTimeRange = getAlertSummaryTimeRange( + { + from: timeRange.from, + to: timeRange.to, + }, + bucketSize?.intervalString ?? DEFAULT_INTERVAL, + bucketSize?.dateFormat ?? DEFAULT_DATE_FORMAT ); return ( diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx index 4642e315e9c54..c53f7c7c73d20 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/components/slo_alerts_table.tsx @@ -9,7 +9,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import type { TimeRange } from '@kbn/es-query'; import { ALL_VALUE } from '@kbn/slo-schema'; import { AlertsTableStateProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alerts_table/alerts_table_state'; -import { SloEmbeddableDeps } from '../slo_alerts_embeddable'; +import { SloEmbeddableDeps } from '../types'; import type { SloItem } from '../types'; import { SLO_ALERTS_TABLE_CONFIG_ID } from '../../constants'; @@ -99,7 +99,6 @@ export function SloAlertsTable({ const { triggersActionsUi: { alertsTableConfigurationRegistry, getAlertsStateTable: AlertsStateTable }, } = deps; - return ( { - public readonly type = SLO_ALERTS_EMBEDDABLE; - private reloadSubject: Subject; - private node?: HTMLElement; - kibanaVersion: string; - private subscription: Subscription; - - constructor( - private readonly deps: SloEmbeddableDeps, - initialInput: SloAlertsEmbeddableInput, - kibanaVersion: string, - parent?: IContainer - ) { - super(initialInput, {}, parent); - this.deps = deps; - this.kibanaVersion = kibanaVersion; - this.reloadSubject = new Subject(); - - this.subscription = this.getInput$().subscribe((input) => { - this.reloadSubject.next(input); - }); - - this.setTitle( - this.input.title || - i18n.translate('xpack.slo.sloAlertsEmbeddable.displayTitle', { - defaultMessage: 'SLO Alerts', - }) - ); - } - public onRenderComplete() { - this.renderComplete.dispatchComplete(); - } - - public getSloAlertsConfig() { - return this.getInput(); - } - - public updateSloAlertsConfig(next: SloAlertsEmbeddableInput) { - this.updateInput(next); - } - - setTitle(title: string) { - this.updateInput({ title }); - } - - public render(node: HTMLElement) { - super.render(node); - this.node = node; - // required for the export feature to work - this.node.setAttribute('data-shared-item', ''); - - const queryClient = new QueryClient(); - - const I18nContext = this.deps.i18n.Context; - const { - slos, - timeRange = { from: 'now-15m/m', to: 'now' }, - showAllGroupByInstances, - } = this.getInput(); - - const deps = this.deps; - const kibanaVersion = this.kibanaVersion; - ReactDOM.render( - - - - - this.onRenderComplete()} - embeddable={this} - deps={deps} - slos={slos} - timeRange={timeRange} - reloadSubject={this.reloadSubject} - showAllGroupByInstances={showAllGroupByInstances} - /> - - - - , - node - ); - } - - public reload() { - this.reloadSubject?.next(undefined); - } - - public destroy() { - super.destroy(); - this.subscription.unsubscribe(); - if (this.node) { - ReactDOM.unmountComponentAtNode(this.node); - } - } -} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts deleted file mode 100644 index 64d0a0755a0e7..0000000000000 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.ts +++ /dev/null @@ -1,71 +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 type { CoreSetup } from '@kbn/core/public'; -import { - EmbeddableFactory, - EmbeddableFactoryDefinition, - ErrorEmbeddable, - IContainer, -} from '@kbn/embeddable-plugin/public'; -import { COMMON_SLO_GROUPING } from '../common/constants'; -import { SLO_ALERTS_EMBEDDABLE, SLOAlertsEmbeddable } from './slo_alerts_embeddable'; -import { SloPublicPluginsStart, SloPublicStart } from '../../..'; -import { SloAlertsEmbeddableInput } from './types'; - -export type SloAlertsEmbeddableFactory = EmbeddableFactory; -export class SloAlertsEmbeddableFactoryDefinition implements EmbeddableFactoryDefinition { - public readonly type = SLO_ALERTS_EMBEDDABLE; - - public readonly grouping = COMMON_SLO_GROUPING; - - constructor( - private getStartServices: CoreSetup['getStartServices'], - private kibanaVersion: string - ) {} - - public async isEditable() { - return true; - } - - public async getExplicitInput(): Promise> { - const [coreStart, pluginStart] = await this.getStartServices(); - try { - const { resolveEmbeddableSloUserInput } = await import('./handle_explicit_input'); - return await resolveEmbeddableSloUserInput(coreStart, pluginStart); - } catch (e) { - return Promise.reject(); - } - } - - public async create(initialInput: SloAlertsEmbeddableInput, parent?: IContainer) { - try { - const [coreStart, pluginsStart] = await this.getStartServices(); - const deps = { ...coreStart, ...pluginsStart }; - return new SLOAlertsEmbeddable(deps, initialInput, this.kibanaVersion, parent); - } catch (e) { - return new ErrorEmbeddable(e, initialInput, parent); - } - } - - public getDescription() { - return i18n.translate('xpack.slo.sloAlertsEmbeddable.description', { - defaultMessage: 'Get an overview of your SLO alerts', - }); - } - - public getDisplayName() { - return i18n.translate('xpack.slo.sloAlertsEmbeddable.displayName', { - defaultMessage: 'SLO Alerts', - }); - } - - public getIconType() { - return 'alert'; - } -} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx new file mode 100644 index 0000000000000..7472c43253454 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_embeddable_factory.tsx @@ -0,0 +1,133 @@ +/* + * 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 React, { useEffect } from 'react'; +import { Router } from '@kbn/shared-ux-router'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { ReactEmbeddableFactory } from '@kbn/embeddable-plugin/public'; +import { + initializeTitles, + useBatchedPublishingSubjects, + fetch$, + FetchContext, + useFetchContext, +} from '@kbn/presentation-publishing'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { createBrowserHistory } from 'history'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { SLO_ALERTS_EMBEDDABLE_ID } from './constants'; +import { SloEmbeddableDeps, SloAlertsEmbeddableState, SloAlertsApi } from './types'; +import { SloAlertsWrapper } from './slo_alerts_wrapper'; +const history = createBrowserHistory(); +const queryClient = new QueryClient(); + +export const getAlertsPanelTitle = () => + i18n.translate('xpack.slo.sloAlertsEmbeddable.displayTitle', { + defaultMessage: 'SLO Alerts', + }); + +export function getAlertsEmbeddableFactory(deps: SloEmbeddableDeps, kibanaVersion: string) { + const factory: ReactEmbeddableFactory = { + type: SLO_ALERTS_EMBEDDABLE_ID, + deserializeState: (state) => { + return state.rawState as SloAlertsEmbeddableState; + }, + buildEmbeddable: async (state, buildApi, uuid, parentApi) => { + const { titlesApi, titleComparators, serializeTitles } = initializeTitles(state); + const defaultTitle$ = new BehaviorSubject(getAlertsPanelTitle()); + const slos$ = new BehaviorSubject(state.slos); + const showAllGroupByInstances$ = new BehaviorSubject(state.showAllGroupByInstances); + const reload$ = new Subject(); + const api = buildApi( + { + ...titlesApi, + defaultPanelTitle: defaultTitle$, + serializeState: () => { + return { + rawState: { + ...serializeTitles(), + slos: slos$.getValue(), + showAllGroupByInstances: showAllGroupByInstances$.getValue(), + }, + }; + }, + getSloAlertsConfig: () => { + return { + slos: slos$.getValue(), + showAllGroupByInstances: showAllGroupByInstances$.getValue(), + }; + }, + updateSloAlertsConfig: (update) => { + slos$.next(update.slos); + showAllGroupByInstances$.next(update.showAllGroupByInstances); + }, + }, + { + slos: [slos$, (value) => slos$.next(value)], + showAllGroupByInstances: [ + showAllGroupByInstances$, + (value) => showAllGroupByInstances$.next(value), + ], + ...titleComparators, + } + ); + + const fetchSubscription = fetch$(api) + .pipe() + .subscribe((next) => { + reload$.next(next); + }); + + return { + api, + Component: () => { + const [slos, showAllGroupByInstances] = useBatchedPublishingSubjects( + slos$, + showAllGroupByInstances$ + ); + const fetchContext = useFetchContext(api); + const I18nContext = deps.i18n.Context; + + useEffect(() => { + return () => { + fetchSubscription.unsubscribe(); + }; + }, []); + return ( + + + + + + + + + + ); + }, + }; + }, + }; + + return factory; +} diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx similarity index 82% rename from x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx rename to x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx index 54af69acbe13d..69529270b23b0 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/handle_explicit_input.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_open_configuration.tsx @@ -4,21 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - import React from 'react'; -import { toMountPoint } from '@kbn/react-kibana-mount'; - import type { CoreStart } from '@kbn/core/public'; -import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import type { EmbeddableSloProps, SloAlertsEmbeddableInput } from './types'; - -import { SloPublicPluginsStart } from '../../../types'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { SloPublicPluginsStart } from '../../..'; import { SloConfiguration } from './slo_configuration'; -export async function resolveEmbeddableSloUserInput( +import type { EmbeddableSloProps } from './types'; +export async function openSloConfiguration( coreStart: CoreStart, pluginStart: SloPublicPluginsStart, - input?: SloAlertsEmbeddableInput + initialState?: EmbeddableSloProps ): Promise { const { overlays } = coreStart; const queryClient = new QueryClient(); @@ -34,15 +31,14 @@ export async function resolveEmbeddableSloUserInput( > { modalSession.close(); resolve(update); }} onCancel={() => { modalSession.close(); - // @ts-expect-error - resolve(undefined); + reject(); }} /> diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx index 413f92418d2cb..9a56bcf0ae0bd 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/slo_alerts_wrapper.tsx @@ -10,37 +10,35 @@ import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; import type { TimeRange } from '@kbn/es-query'; import { CONTEXT_MENU_TRIGGER } from '@kbn/embeddable-plugin/public'; -import { IEmbeddable, EmbeddableOutput } from '@kbn/embeddable-plugin/public'; import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; import { Subject } from 'rxjs'; import styled from 'styled-components'; import { observabilityPaths } from '@kbn/observability-plugin/common'; +import { FetchContext } from '@kbn/presentation-publishing'; import { SloIncludedCount } from './components/slo_included_count'; import { SloAlertsSummary } from './components/slo_alerts_summary'; import { SloAlertsTable } from './components/slo_alerts_table'; -import type { SloItem } from './types'; -import { SloEmbeddableDeps } from './slo_alerts_embeddable'; -import { SloAlertsEmbeddableInput } from './types'; +import type { SloItem, SloEmbeddableDeps } from './types'; import { EDIT_SLO_ALERTS_ACTION } from '../../../ui_actions/edit_slo_alerts_panel'; interface Props { deps: SloEmbeddableDeps; slos: SloItem[]; timeRange: TimeRange; - embeddable: IEmbeddable; + embeddable: any; onRenderComplete?: () => void; - reloadSubject: Subject; + reloadSubject: Subject; showAllGroupByInstances?: boolean; } export function SloAlertsWrapper({ embeddable, - slos: initialSlos, + slos, deps, timeRange: initialTimeRange, onRenderComplete, reloadSubject, - showAllGroupByInstances: initialShowAllGroupByInstances, + showAllGroupByInstances, }: Props) { const { application: { navigateToUrl }, @@ -48,24 +46,15 @@ export function SloAlertsWrapper({ } = deps; const [timeRange, setTimeRange] = useState(initialTimeRange); - const [slos, setSlos] = useState(initialSlos); - const [showAllGroupByInstances, setShowAllGroupByInstances] = useState( - initialShowAllGroupByInstances - ); - const [lastRefreshTime, setLastRefreshTime] = useState(undefined); useEffect(() => { const subs = reloadSubject?.subscribe((input) => { if (input) { - const { timeRange: nTimeRange, slos: nSlos } = input; - - setSlos(nSlos); - + const { timeRange: nTimeRange } = input; if (nTimeRange && (nTimeRange.from !== timeRange.from || nTimeRange.to !== timeRange.to)) { setTimeRange(nTimeRange); } - setShowAllGroupByInstances(input.showAllGroupByInstances); } setLastRefreshTime(Date.now()); }); @@ -100,10 +89,10 @@ export function SloAlertsWrapper({ },rangeTo:${timeRange.to})` ); }; - return ( ; + initialInput?: EmbeddableSloProps; onCreate: (props: EmbeddableSloProps) => void; onCancel: () => void; } diff --git a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts index 1483dc4898610..8b660442ca7b6 100644 --- a/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/embeddable/slo/alerts/types.ts @@ -4,8 +4,27 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EmbeddableInput } from '@kbn/embeddable-plugin/public'; -import type { TimeRange } from '@kbn/es-query'; +import { DefaultEmbeddableApi, EmbeddableInput } from '@kbn/embeddable-plugin/public'; +import { + type CoreStart, + IUiSettingsClient, + ApplicationStart, + NotificationsStart, +} from '@kbn/core/public'; +import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { CasesPublicStart } from '@kbn/cases-plugin/public'; +import { SettingsStart } from '@kbn/core-ui-settings-browser'; +import { SecurityPluginStart } from '@kbn/security-plugin/public'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; +import { ServerlessPluginStart } from '@kbn/serverless/public'; +import { + SerializedTitles, + PublishesWritablePanelTitle, + PublishesPanelTitle, + EmbeddableApiContext, +} from '@kbn/presentation-publishing'; export interface SloItem { id: string; @@ -16,16 +35,21 @@ export interface SloItem { export interface EmbeddableSloProps { slos: SloItem[]; - timeRange?: TimeRange; - lastReloadRequestTime?: number | undefined; showAllGroupByInstances?: boolean; } export type SloAlertsEmbeddableInput = EmbeddableInput & EmbeddableSloProps; +export type SloAlertsEmbeddableState = SerializedTitles & EmbeddableSloProps; + +export type SloAlertsApi = DefaultEmbeddableApi & + PublishesWritablePanelTitle & + PublishesPanelTitle & + HasSloAlertsConfig; + export interface HasSloAlertsConfig { - getSloAlertsConfig: () => SloAlertsEmbeddableInput; - updateSloAlertsConfig: (next: SloAlertsEmbeddableInput) => void; + getSloAlertsConfig: () => EmbeddableSloProps; + updateSloAlertsConfig: (next: EmbeddableSloProps) => void; } export const apiHasSloAlertsConfig = (api: unknown | null): api is HasSloAlertsConfig => { @@ -35,3 +59,23 @@ export const apiHasSloAlertsConfig = (api: unknown | null): api is HasSloAlertsC typeof (api as HasSloAlertsConfig).updateSloAlertsConfig === 'function' ); }; + +export type SloAlertsEmbeddableActionContext = EmbeddableApiContext & { + embeddable: SloAlertsApi; +}; + +export interface SloEmbeddableDeps { + uiSettings: IUiSettingsClient; + http: CoreStart['http']; + i18n: CoreStart['i18n']; + application: ApplicationStart; + triggersActionsUi: TriggersAndActionsUIPublicPluginStart; + data: DataPublicPluginStart; + notifications: NotificationsStart; + cases: CasesPublicStart; + settings: SettingsStart; + security: SecurityPluginStart; + charts: ChartsPluginStart; + uiActions: UiActionsStart; + serverless?: ServerlessPluginStart; +} diff --git a/x-pack/plugins/observability_solution/slo/public/plugin.ts b/x-pack/plugins/observability_solution/slo/public/plugin.ts index 99f586790cd99..e387a7f85a7e3 100644 --- a/x-pack/plugins/observability_solution/slo/public/plugin.ts +++ b/x-pack/plugins/observability_solution/slo/public/plugin.ts @@ -28,6 +28,8 @@ import { ExperimentalFeatures, SloConfig } from '../common/config'; import { SLO_OVERVIEW_EMBEDDABLE_ID } from './embeddable/slo/overview/constants'; import { SloOverviewEmbeddableState } from './embeddable/slo/overview/types'; import { SLO_ERROR_BUDGET_ID } from './embeddable/slo/error_budget/constants'; +import { SLO_ALERTS_EMBEDDABLE_ID } from './embeddable/slo/alerts/constants'; + export class SloPlugin implements Plugin { @@ -113,17 +115,19 @@ export class SloPlugin return getOverviewEmbeddableFactory(deps); } ); - const registerSloAlertsEmbeddableFactory = async () => { - const { SloAlertsEmbeddableFactoryDefinition } = await import( - './embeddable/slo/alerts/slo_alerts_embeddable_factory' - ); - const factory = new SloAlertsEmbeddableFactoryDefinition( - coreSetup.getStartServices, - kibanaVersion - ); - pluginsSetup.embeddable.registerEmbeddableFactory(factory.type, factory); - }; - registerSloAlertsEmbeddableFactory(); + + pluginsSetup.embeddable.registerReactEmbeddableFactory( + SLO_ALERTS_EMBEDDABLE_ID, + async () => { + const deps = { ...coreStart, ...pluginsStart }; + + const { getAlertsEmbeddableFactory } = await import( + './embeddable/slo/alerts/slo_alerts_embeddable_factory' + ); + + return getAlertsEmbeddableFactory(deps, kibanaVersion); + } + ); pluginsSetup.embeddable.registerReactEmbeddableFactory(SLO_ERROR_BUDGET_ID, async () => { const deps = { ...coreStart, ...pluginsStart }; diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx new file mode 100644 index 0000000000000..b365881bf915a --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/create_alerts_panel_action.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import type { CoreSetup } from '@kbn/core/public'; +import { apiIsPresentationContainer } from '@kbn/presentation-containers'; +import { + IncompatibleActionError, + type UiActionsActionDefinition, +} from '@kbn/ui-actions-plugin/public'; +import { EmbeddableApiContext } from '@kbn/presentation-publishing'; +import { + ADD_SLO_ALERTS_ACTION_ID, + SLO_ALERTS_EMBEDDABLE_ID, +} from '../embeddable/slo/alerts/constants'; +import { SloPublicPluginsStart, SloPublicStart } from '..'; +import { COMMON_SLO_GROUPING } from '../embeddable/slo/common/constants'; + +export function createAddAlertsPanelAction( + getStartServices: CoreSetup['getStartServices'] +): UiActionsActionDefinition { + return { + id: ADD_SLO_ALERTS_ACTION_ID, + grouping: COMMON_SLO_GROUPING, + getIconType: () => 'alert', + isCompatible: async ({ embeddable }) => { + return apiIsPresentationContainer(embeddable); + }, + execute: async ({ embeddable }) => { + if (!apiIsPresentationContainer(embeddable)) throw new IncompatibleActionError(); + const [coreStart, deps] = await getStartServices(); + try { + const { openSloConfiguration } = await import( + '../embeddable/slo/alerts/slo_alerts_open_configuration' + ); + const initialState = await openSloConfiguration(coreStart, deps); + embeddable.addNewPanel( + { + panelType: SLO_ALERTS_EMBEDDABLE_ID, + initialState, + }, + true + ); + } catch (e) { + return Promise.reject(); + } + }, + getDisplayName: () => + i18n.translate('xpack.slo.sloAlertsEmbeddable.displayName', { + defaultMessage: 'SLO Alerts', + }), + }; +} diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx b/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx index 454833a26af94..ce9b4d196ffb5 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/edit_slo_alerts_panel.tsx @@ -17,26 +17,27 @@ import { CanAccessViewMode, HasType, } from '@kbn/presentation-publishing'; -import { createAction } from '@kbn/ui-actions-plugin/public'; -import type { SLOAlertsEmbeddable } from '../embeddable/slo/alerts/slo_alerts_embeddable'; -import { SLO_ALERTS_EMBEDDABLE } from '../embeddable/slo/constants'; +import { type UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import { SLO_ALERTS_EMBEDDABLE_ID } from '../embeddable/slo/alerts/constants'; import { SloPublicPluginsStart, SloPublicStart } from '..'; -import { HasSloAlertsConfig } from '../embeddable/slo/alerts/types'; - +import { + HasSloAlertsConfig, + SloAlertsEmbeddableActionContext, +} from '../embeddable/slo/alerts/types'; export const EDIT_SLO_ALERTS_ACTION = 'editSloAlertsPanelAction'; type EditSloAlertsPanelApi = CanAccessViewMode & HasType & HasSloAlertsConfig; const isEditSloAlertsPanelApi = (api: unknown): api is EditSloAlertsPanelApi => Boolean( apiHasType(api) && - apiIsOfType(api, SLO_ALERTS_EMBEDDABLE) && + apiIsOfType(api, SLO_ALERTS_EMBEDDABLE_ID) && apiCanAccessViewMode(api) && getInheritedViewMode(api) === ViewMode.EDIT ); export function createEditSloAlertsPanelAction( getStartServices: CoreSetup['getStartServices'] -) { - return createAction({ +): UiActionsActionDefinition { + return { id: EDIT_SLO_ALERTS_ACTION, type: EDIT_SLO_ALERTS_ACTION, getIconType(): string { @@ -46,7 +47,7 @@ export function createEditSloAlertsPanelAction( i18n.translate('xpack.slo.actions.editSloAlertsEmbeddableTitle', { defaultMessage: 'Edit configuration', }), - async execute({ embeddable }: EmbeddableApiContext) { + async execute({ embeddable }) { if (!embeddable) { throw new Error('Not possible to execute an action without the embeddable context'); } @@ -54,21 +55,21 @@ export function createEditSloAlertsPanelAction( const [coreStart, pluginStart] = await getStartServices(); try { - const { resolveEmbeddableSloUserInput } = await import( - '../embeddable/slo/alerts/handle_explicit_input' + const { openSloConfiguration } = await import( + '../embeddable/slo/alerts/slo_alerts_open_configuration' ); - const result = await resolveEmbeddableSloUserInput( + const result = await openSloConfiguration( coreStart, pluginStart, - (embeddable as SLOAlertsEmbeddable).getSloAlertsConfig() + embeddable.getSloAlertsConfig() ); - (embeddable as SLOAlertsEmbeddable).updateInput(result); + embeddable.updateSloAlertsConfig(result); } catch (e) { return Promise.reject(); } }, isCompatible: async ({ embeddable }: EmbeddableApiContext) => isEditSloAlertsPanelApi(embeddable), - }); + }; } diff --git a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts index e544a36fd84f7..76862a3afe90d 100644 --- a/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts +++ b/x-pack/plugins/observability_solution/slo/public/ui_actions/index.ts @@ -12,6 +12,7 @@ import { createEditSloAlertsPanelAction } from './edit_slo_alerts_panel'; import { createEditSloOverviewPanelAction } from './edit_slo_overview_panel'; import { createOverviewPanelAction } from './create_overview_panel_action'; import { createAddErrorBudgetPanelAction } from './create_error_budget_action'; +import { createAddAlertsPanelAction } from './create_alerts_panel_action'; import { SloPublicPluginsStart, SloPublicStart } from '..'; export function registerSloUiActions( @@ -23,10 +24,12 @@ export function registerSloUiActions( const editSloOverviewPanelAction = createEditSloOverviewPanelAction(core.getStartServices); const addOverviewPanelAction = createOverviewPanelAction(core.getStartServices); const addErrorBudgetPanelAction = createAddErrorBudgetPanelAction(core.getStartServices); + const addAlertsPanelAction = createAddAlertsPanelAction(core.getStartServices); // Assign triggers uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editSloAlertsPanelAction); uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editSloOverviewPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addOverviewPanelAction); uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addErrorBudgetPanelAction); + uiActions.addTriggerAction('ADD_PANEL_TRIGGER', addAlertsPanelAction); } From a9c8e8f33b30547c815fb598943c354fcd4ee6c2 Mon Sep 17 00:00:00 2001 From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com> Date: Mon, 27 May 2024 16:34:09 -0400 Subject: [PATCH 22/59] [Index management] Fix to enable adding new fields with new inference endpoint created from inference Flyout (#183869) ## Summary Fixes bug, enabling adding a new fields with inference endpoints created from inference flyout. **Background** This [PR](https://github.com/elastic/kibana/pull/180330) introduced a package `@kbn/inference_integration_flyout` to enable adding a new inference endpoint with Elasticsearch models - ELSER & E5, Third party model - HuggingFace, Cohere, OpenAI and uploading via ELAND instructions in a Flyout. This flyout component is used by Add field component, when it's a `semantic_text` field and would like to create a new inference endpoint id for the field. **Bug description** After a new inference endpoint is created, Cannot add field with this inference endpoint . https://github.com/elastic/kibana/assets/55930906/ad62fdbb-4ef6-40af-9812-5d91b7e63c26 **Expected** Should be able to add a new field with this new inference endpoint **Testing instructions** **Elasticsearch changes (only to test save mappings)** Since ES changes for the semantic_text has been merged to main, this can be tested against running ES from source or from latest snapshot 1. Update local branch with latest Elasticsearch changes from main 2. Run the elasticsearch: `./gradlew :run -Drun.license_type=trial` **Manual test in UI** 1. Set is [isSemanticTextEnabled](https://github.com/elastic/kibana/blob/e89b991d7473caba2a3a5b6204080f50100c67b9/x-pack/plugins/index_management/public/application/sections/home/index_list/details_page/details_page_mappings_content.tsx#L72) to true 4. Add a new field with type - `Semantic_text` 5. Click on drop down menu below `Select an inference endpoint:` 6. Click `Add inference Endpoint` 7. create a new inference endpoint in Add inference endpoint flyout 8. the drop down menu list, from Step 3, should have the new inference endpoint created on Step 5 9. Fill in the reference field and Field name 10. click add field 11. A new field should be created using this inference endpoint --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../select_inference_id.test.tsx | 3 ++ .../field_parameters/select_inference_id.tsx | 31 +++++++++++++++++-- .../fields/create_field/create_field.tsx | 31 ++++++++++++++++--- ..._details_page_mappings_model_management.ts | 13 +++++--- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx index ce516063e3449..4222cf493a9bf 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.test.tsx @@ -11,6 +11,7 @@ import { SelectInferenceId } from './select_inference_id'; const onChangeMock = jest.fn(); const setValueMock = jest.fn(); +const setNewInferenceEndpointMock = jest.fn(); jest.mock('../../../../../app_context', () => ({ useAppContext: jest.fn().mockReturnValue({ @@ -21,6 +22,7 @@ jest.mock('../../../../../app_context', () => ({ mlApi: { trainedModels: { getTrainedModels: jest.fn().mockResolvedValue([]), + getTrainedModelStats: jest.fn().mockResolvedValue([]), }, }, }, @@ -38,6 +40,7 @@ describe('SelectInferenceId', () => { onChange: onChangeMock, 'data-test-subj': 'data-inference-endpoint-list', setValue: setValueMock, + setNewInferenceEndpoint: setNewInferenceEndpointMock, }, memoryRouter: { wrapComponent: false }, }); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx index f08007f3174df..1d09c2469c164 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/select_inference_id.tsx @@ -30,7 +30,11 @@ import { TRAINED_MODEL_TYPE, } from '@kbn/ml-trained-models-utils'; import { InferenceTaskType } from '@elastic/elasticsearch/lib/api/types'; -import { ModelConfig } from '@kbn/inference_integration_flyout/types'; +import { + ElasticsearchModelDefaultOptions, + ModelConfig, + Service, +} from '@kbn/inference_integration_flyout/types'; import { FormattedMessage } from '@kbn/i18n-react'; import { InferenceFlyoutWrapper } from '@kbn/inference_integration_flyout/components/inference_flyout_wrapper'; import { TrainedModelConfigResponse } from '@kbn/ml-plugin/common/types/trained_models'; @@ -39,15 +43,26 @@ import { getFieldConfig } from '../../../lib'; import { useAppContext } from '../../../../../app_context'; import { Form, UseField, useForm } from '../../../shared_imports'; import { useLoadInferenceModels } from '../../../../../services/api'; +import { getTrainedModelStats } from '../../../../../../hooks/use_details_page_mappings_model_management'; +import { InferenceToModelIdMap } from '../fields'; + +const inferenceServiceTypeElasticsearchModelMap: Record = + { + elser: ElasticsearchModelDefaultOptions.elser, + elasticsearch: ElasticsearchModelDefaultOptions.e5, + }; + interface Props { onChange(value: string): void; 'data-test-subj'?: string; setValue: (value: string) => void; + setNewInferenceEndpoint: (newInferenceEndpoint: InferenceToModelIdMap) => void; } export const SelectInferenceId = ({ onChange, 'data-test-subj': dataTestSubj, setValue, + setNewInferenceEndpoint, }: Props) => { const { core: { application }, @@ -135,14 +150,26 @@ export const SelectInferenceId = ({ setIsInferenceFlyoutVisible(!isInferenceFlyoutVisible); setIsCreateInferenceApiLoading(false); setInferenceAddError(undefined); + const trainedModelStats = await ml?.mlApi?.trainedModels.getTrainedModelStats(); + const defaultEndpointId = + inferenceServiceTypeElasticsearchModelMap[modelConfig.service] || ''; + const newModelId: InferenceToModelIdMap = {}; + newModelId[inferenceId] = { + trainedModelId: defaultEndpointId, + isDeployable: + modelConfig.service === Service.elser || modelConfig.service === Service.elasticsearch, + isDeployed: getTrainedModelStats(trainedModelStats)[defaultEndpointId] === 'deployed', + defaultInferenceEndpoint: false, + }; resendRequest(); + setNewInferenceEndpoint(newModelId); } catch (error) { const errorObj = extractErrorProperties(error); setInferenceAddError(errorObj.message); setIsCreateInferenceApiLoading(false); } }, - [isInferenceFlyoutVisible, resendRequest, ml] + [isInferenceFlyoutVisible, resendRequest, ml, setNewInferenceEndpoint] ); useEffect(() => { const subscription = subscribe((updateData) => { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx index 7c5e978404383..f91be7bf55fe2 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx @@ -16,10 +16,10 @@ import { import { i18n } from '@kbn/i18n'; import { MlPluginStart } from '@kbn/ml-plugin/public'; import classNames from 'classnames'; -import React, { useEffect } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EUI_SIZE, TYPE_DEFINITION } from '../../../../constants'; import { fieldSerializer } from '../../../../lib'; -import { useDispatch } from '../../../../mappings_state_context'; +import { useDispatch, useMappingsState } from '../../../../mappings_state_context'; import { Form, FormDataProvider, UseField, useForm, useFormData } from '../../../../shared_imports'; import { Field, MainType, NormalizedFields } from '../../../../types'; import { NameParameter, SubTypeParameter, TypeParameter } from '../../field_parameters'; @@ -70,7 +70,6 @@ export const CreateField = React.memo(function CreateFieldComponent({ }: Props) { const { isSemanticTextEnabled, indexName, ml, setErrorsInTrainedModelDeployment } = semanticTextInfo ?? {}; - const dispatch = useDispatch(); const { form } = useForm({ @@ -315,8 +314,26 @@ interface InferenceProps { } function InferenceIdCombo({ setValue }: InferenceProps) { + const { inferenceToModelIdMap } = useMappingsState(); + const dispatch = useDispatch(); const [{ type }] = useFormData({ watch: 'type' }); + // update new inferenceEndpoint + const setNewInferenceEndpoint = useCallback( + (newInferenceEndpoint: InferenceToModelIdMap) => { + dispatch({ + type: 'inferenceToModelIdMap.update', + value: { + inferenceToModelIdMap: { + ...inferenceToModelIdMap, + ...newInferenceEndpoint, + }, + }, + }); + }, + [dispatch, inferenceToModelIdMap] + ); + if (type === undefined || type[0]?.value !== 'semantic_text') { return null; } @@ -325,7 +342,13 @@ function InferenceIdCombo({ setValue }: InferenceProps) { <> - {(field) => } + {(field) => ( + + )} ); diff --git a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts index 559fb6a11f13f..fb27053d35547 100644 --- a/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts +++ b/x-pack/plugins/index_management/public/hooks/use_details_page_mappings_model_management.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { ElasticsearchModelDefaultOptions, Service } from '@kbn/inference_integration_flyout/types'; import { InferenceStatsResponse } from '@kbn/ml-plugin/public/application/services/ml_api_service/trained_models'; import type { InferenceAPIConfigResponse } from '@kbn/ml-trained-models-utils'; import { useCallback, useMemo } from 'react'; @@ -27,12 +28,16 @@ const getCustomInferenceIdMap = ( ) => { return models?.data.reduce((inferenceMap, model) => { const inferenceId = model.model_id; - const trainedModelId = - 'model_id' in model.service_settings ? model.service_settings.model_id : ''; + const trainedModelId = + 'model_id' in model.service_settings && + (model.service_settings.model_id === ElasticsearchModelDefaultOptions.elser || + model.service_settings.model_id === ElasticsearchModelDefaultOptions.e5) + ? model.service_settings.model_id + : ''; inferenceMap[inferenceId] = { trainedModelId, - isDeployable: model.service === 'elser' || model.service === 'elasticsearch', + isDeployable: model.service === Service.elser || model.service === Service.elasticsearch, isDeployed: deploymentStatsByModelId[trainedModelId] === 'deployed', defaultInferenceEndpoint: false, }; @@ -40,7 +45,7 @@ const getCustomInferenceIdMap = ( }, {}); }; -const getTrainedModelStats = (modelStats?: InferenceStatsResponse): DeploymentStatusType => { +export const getTrainedModelStats = (modelStats?: InferenceStatsResponse): DeploymentStatusType => { return ( modelStats?.trained_model_stats.reduce((acc, modelStat) => { if (modelStat.model_id) { From e595d28e00485e8b54e4de55535694bd8a3fb8d3 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 27 May 2024 19:00:15 -0700 Subject: [PATCH 23/59] [HTTP/OAS] Add descriptions for upgrade assistant APIs (#184269) --- .../routes/reindex_indices/batch_reindex_indices.ts | 8 ++++++++ .../server/routes/reindex_indices/reindex_indices.ts | 12 ++++++++++++ .../upgrade_assistant/server/routes/status.ts | 4 ++++ 3 files changed, 24 insertions(+) diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts index c9a8eedceffe3..391306aa18723 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/batch_reindex_indices.ts @@ -36,6 +36,10 @@ export function registerBatchReindexIndicesRoutes( router.get( { path: `${BASE_PATH}/batch/queue`, + options: { + access: 'public', + description: `Get the batch reindex queue`, + }, validate: {}, }, versionCheckHandlerWrapper(async ({ core }, request, response) => { @@ -71,6 +75,10 @@ export function registerBatchReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/batch`, + options: { + access: 'public', + description: `Batch start or resume reindex`, + }, validate: { body: schema.object({ indexNames: schema.arrayOf(schema.string()), diff --git a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts index 0ffa9335c4de7..9b2b046169b77 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/reindex_indices/reindex_indices.ts @@ -34,6 +34,10 @@ export function registerReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/{indexName}`, + options: { + access: 'public', + description: `Start or resume reindex`, + }, validate: { params: schema.object({ indexName: schema.string(), @@ -77,6 +81,10 @@ export function registerReindexIndicesRoutes( router.get( { path: `${BASE_PATH}/{indexName}`, + options: { + access: 'public', + description: `Get reindex status`, + }, validate: { params: schema.object({ indexName: schema.string(), @@ -134,6 +142,10 @@ export function registerReindexIndicesRoutes( router.post( { path: `${BASE_PATH}/{indexName}/cancel`, + options: { + access: 'public', + description: `Cancel reindex`, + }, validate: { params: schema.object({ indexName: schema.string(), diff --git a/x-pack/plugins/upgrade_assistant/server/routes/status.ts b/x-pack/plugins/upgrade_assistant/server/routes/status.ts index 3f6f90d8823f0..7a44981776319 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/status.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/status.ts @@ -24,6 +24,10 @@ export function registerUpgradeStatusRoute({ router.get( { path: `${API_BASE_PATH}/status`, + options: { + access: 'public', + description: `Get upgrade readiness status`, + }, validate: false, }, versionCheckHandlerWrapper(async ({ core }, request, response) => { From 3af774f96d4114d1053cd30e058a00d1f406b389 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 28 May 2024 01:07:56 -0400 Subject: [PATCH 24/59] [api-docs] 2024-05-28 Daily api_docs build (#184327) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/720 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- .../ai_assistant_management_selection.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/apm_data_access.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/assets_data_access.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.devdocs.json | 4 +- api_docs/data.mdx | 4 +- api_docs/data_query.mdx | 4 +- api_docs/data_search.devdocs.json | 15 + api_docs/data_search.mdx | 4 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.devdocs.json | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/dataset_quality.mdx | 2 +- api_docs/deprecations_by_api.mdx | 4 +- api_docs/deprecations_by_plugin.mdx | 5 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/discover_shared.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/elastic_assistant.mdx | 2 +- api_docs/embeddable.devdocs.json | 4 - api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_annotation_listing.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.devdocs.json | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/ingest_pipelines.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_actions_types.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_log_pattern_analysis.mdx | 2 +- api_docs/kbn_aiops_log_rate_analysis.mdx | 2 +- .../kbn_alerting_api_integration_helpers.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerting_types.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- api_docs/kbn_analytics_collection_utils.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_data_view.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_bfetch_error.mdx | 2 +- api_docs/kbn_calculate_auto.mdx | 2 +- .../kbn_calculate_width_from_char_count.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mock.mdx | 2 +- api_docs/kbn_code_owners.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- .../kbn_content_management_content_editor.mdx | 2 +- ...tent_management_tabbed_table_list_view.mdx | 2 +- ...kbn_content_management_table_list_view.mdx | 2 +- ...tent_management_table_list_view_common.mdx | 2 +- ...ntent_management_table_list_view_table.mdx | 2 +- api_docs/kbn_content_management_utils.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- .../kbn_core_application_browser_internal.mdx | 2 +- .../kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- .../kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- .../kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- ..._core_custom_branding_browser_internal.mdx | 2 +- ...kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- ...n_core_custom_branding_server_internal.mdx | 2 +- .../kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- ...kbn_core_deprecations_browser_internal.mdx | 2 +- .../kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- .../kbn_core_deprecations_server_internal.mdx | 2 +- .../kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- ...e_elasticsearch_client_server_internal.mdx | 2 +- ...core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- ...kbn_core_elasticsearch_server_internal.mdx | 2 +- .../kbn_core_elasticsearch_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- .../kbn_core_http_context_server_mocks.mdx | 2 +- ...re_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- ...bn_core_http_resources_server_internal.mdx | 2 +- .../kbn_core_http_resources_server_mocks.mdx | 2 +- .../kbn_core_http_router_server_internal.mdx | 2 +- .../kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- ...kbn_core_integrations_browser_internal.mdx | 2 +- .../kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- ...ore_metrics_collectors_server_internal.mdx | 2 +- ...n_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- ...bn_core_notifications_browser_internal.mdx | 2 +- .../kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- .../kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- .../kbn_core_plugins_contracts_browser.mdx | 2 +- .../kbn_core_plugins_contracts_server.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- .../kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- .../kbn_core_saved_objects_api_browser.mdx | 2 +- .../kbn_core_saved_objects_api_server.mdx | 2 +- ...bn_core_saved_objects_api_server_mocks.mdx | 2 +- ...ore_saved_objects_base_server_internal.mdx | 2 +- ...n_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- ...bn_core_saved_objects_browser_internal.mdx | 2 +- .../kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- ..._objects_import_export_server_internal.mdx | 2 +- ...ved_objects_import_export_server_mocks.mdx | 2 +- ...aved_objects_migration_server_internal.mdx | 2 +- ...e_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- ...kbn_core_saved_objects_server_internal.mdx | 2 +- .../kbn_core_saved_objects_server_mocks.mdx | 2 +- .../kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_security_browser.mdx | 2 +- .../kbn_core_security_browser_internal.mdx | 2 +- api_docs/kbn_core_security_browser_mocks.mdx | 2 +- api_docs/kbn_core_security_common.mdx | 2 +- api_docs/kbn_core_security_server.mdx | 2 +- .../kbn_core_security_server_internal.mdx | 2 +- api_docs/kbn_core_security_server_mocks.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- ...core_test_helpers_deprecations_getters.mdx | 2 +- ...n_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- .../kbn_core_test_helpers_model_versions.mdx | 2 +- ...n_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- .../kbn_core_ui_settings_browser_internal.mdx | 2 +- .../kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- .../kbn_core_ui_settings_server_internal.mdx | 2 +- .../kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- .../kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_browser.mdx | 2 +- ...kbn_core_user_profile_browser_internal.mdx | 2 +- .../kbn_core_user_profile_browser_mocks.mdx | 2 +- api_docs/kbn_core_user_profile_common.mdx | 2 +- api_docs/kbn_core_user_profile_server.mdx | 2 +- .../kbn_core_user_profile_server_internal.mdx | 2 +- .../kbn_core_user_profile_server_mocks.mdx | 2 +- api_docs/kbn_core_user_settings_server.mdx | 2 +- .../kbn_core_user_settings_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_custom_icons.mdx | 2 +- api_docs/kbn_custom_integrations.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_data_forge.mdx | 2 +- api_docs/kbn_data_service.mdx | 2 +- api_docs/kbn_data_stream_adapter.mdx | 2 +- api_docs/kbn_data_view_utils.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_deeplinks_analytics.mdx | 2 +- api_docs/kbn_deeplinks_devtools.mdx | 2 +- api_docs/kbn_deeplinks_fleet.mdx | 2 +- api_docs/kbn_deeplinks_management.mdx | 2 +- api_docs/kbn_deeplinks_ml.mdx | 2 +- api_docs/kbn_deeplinks_observability.mdx | 2 +- api_docs/kbn_deeplinks_search.mdx | 2 +- api_docs/kbn_deeplinks_security.mdx | 2 +- api_docs/kbn_deeplinks_shared.mdx | 2 +- api_docs/kbn_default_nav_analytics.mdx | 2 +- api_docs/kbn_default_nav_devtools.mdx | 2 +- api_docs/kbn_default_nav_management.mdx | 2 +- api_docs/kbn_default_nav_ml.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_discover_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_elastic_agent_utils.mdx | 2 +- api_docs/kbn_elastic_assistant.mdx | 2 +- api_docs/kbn_elastic_assistant_common.mdx | 2 +- api_docs/kbn_entities_schema.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_esql_ast.mdx | 2 +- api_docs/kbn_esql_utils.devdocs.json | 33 +++ api_docs/kbn_esql_utils.mdx | 4 +- api_docs/kbn_esql_validation_autocomplete.mdx | 2 +- api_docs/kbn_event_annotation_common.mdx | 2 +- api_docs/kbn_event_annotation_components.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_field_utils.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_formatters.mdx | 2 +- .../kbn_ftr_common_functional_services.mdx | 2 +- .../kbn_ftr_common_functional_ui_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_console_definitions.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_index_management.mdx | 2 +- api_docs/kbn_inference_integration_flyout.mdx | 2 +- api_docs/kbn_infra_forge.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_ipynb.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- .../kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_lens_embeddable_utils.mdx | 2 +- api_docs/kbn_lens_formula_docs.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_content_badge.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_management_cards_navigation.mdx | 2 +- .../kbn_management_settings_application.mdx | 2 +- ...ent_settings_components_field_category.mdx | 2 +- ...gement_settings_components_field_input.mdx | 2 +- ...nagement_settings_components_field_row.mdx | 2 +- ...bn_management_settings_components_form.mdx | 2 +- ...n_management_settings_field_definition.mdx | 2 +- api_docs/kbn_management_settings_ids.mdx | 2 +- ...n_management_settings_section_registry.mdx | 2 +- api_docs/kbn_management_settings_types.mdx | 2 +- .../kbn_management_settings_utilities.mdx | 2 +- api_docs/kbn_management_storybook_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_maps_vector_tile_utils.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_anomaly_utils.mdx | 2 +- api_docs/kbn_ml_cancellable_search.mdx | 2 +- api_docs/kbn_ml_category_validator.mdx | 2 +- api_docs/kbn_ml_chi2test.mdx | 2 +- .../kbn_ml_data_frame_analytics_utils.mdx | 2 +- api_docs/kbn_ml_data_grid.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_date_utils.mdx | 2 +- api_docs/kbn_ml_error_utils.mdx | 2 +- api_docs/kbn_ml_in_memory_table.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_kibana_theme.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_runtime_field_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_time_buckets.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_ui_actions.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_mock_idp_utils.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- .../kbn_observability_alerting_test_data.mdx | 2 +- ...ility_get_padded_alert_time_range_util.mdx | 2 +- api_docs/kbn_openapi_bundler.mdx | 2 +- api_docs/kbn_openapi_generator.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_panel_loader.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_check.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_presentation_containers.mdx | 2 +- .../kbn_presentation_publishing.devdocs.json | 40 +++ api_docs/kbn_presentation_publishing.mdx | 4 +- api_docs/kbn_profiling_utils.mdx | 2 +- api_docs/kbn_random_sampling.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_react_hooks.mdx | 2 +- api_docs/kbn_react_kibana_context_common.mdx | 2 +- api_docs/kbn_react_kibana_context_render.mdx | 2 +- api_docs/kbn_react_kibana_context_root.mdx | 2 +- api_docs/kbn_react_kibana_context_styled.mdx | 2 +- api_docs/kbn_react_kibana_context_theme.mdx | 2 +- api_docs/kbn_react_kibana_mount.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.mdx | 2 +- api_docs/kbn_reporting_csv_share_panel.mdx | 2 +- api_docs/kbn_reporting_export_types_csv.mdx | 2 +- .../kbn_reporting_export_types_csv_common.mdx | 2 +- api_docs/kbn_reporting_export_types_pdf.mdx | 2 +- .../kbn_reporting_export_types_pdf_common.mdx | 2 +- api_docs/kbn_reporting_export_types_png.mdx | 2 +- .../kbn_reporting_export_types_png_common.mdx | 2 +- api_docs/kbn_reporting_mocks_server.mdx | 2 +- api_docs/kbn_reporting_public.mdx | 2 +- api_docs/kbn_reporting_server.mdx | 2 +- api_docs/kbn_resizable_layout.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_router_to_openapispec.mdx | 2 +- api_docs/kbn_router_utils.mdx | 2 +- api_docs/kbn_rrule.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_search_api_panels.mdx | 2 +- api_docs/kbn_search_connectors.devdocs.json | 259 +++++++++++++++++- api_docs/kbn_search_connectors.mdx | 4 +- api_docs/kbn_search_errors.mdx | 2 +- api_docs/kbn_search_index_documents.mdx | 2 +- api_docs/kbn_search_response_warnings.mdx | 2 +- api_docs/kbn_search_types.mdx | 2 +- api_docs/kbn_security_hardening.mdx | 2 +- api_docs/kbn_security_plugin_types_common.mdx | 2 +- api_docs/kbn_security_plugin_types_public.mdx | 2 +- api_docs/kbn_security_plugin_types_server.mdx | 2 +- api_docs/kbn_security_solution_features.mdx | 2 +- api_docs/kbn_security_solution_navigation.mdx | 2 +- api_docs/kbn_security_solution_side_nav.mdx | 2 +- ...kbn_security_solution_storybook_config.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_data_table.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- ...ritysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_grouping.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_serverless_common_settings.mdx | 2 +- .../kbn_serverless_observability_settings.mdx | 2 +- api_docs/kbn_serverless_project_switcher.mdx | 2 +- api_docs/kbn_serverless_search_settings.mdx | 2 +- api_docs/kbn_serverless_security_settings.mdx | 2 +- api_docs/kbn_serverless_storybook_config.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- .../kbn_shared_ux_button_exit_full_screen.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_chrome_navigation.mdx | 2 +- api_docs/kbn_shared_ux_error_boundary.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- .../kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- ...shared_ux_page_analytics_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...bn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- .../kbn_shared_ux_page_kibana_template.mdx | 2 +- ...n_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- .../kbn_shared_ux_page_no_data_config.mdx | 2 +- ...bn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- ...n_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_tabbed_modal.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_solution_nav_oblt.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_sort_predicates.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_eui_helpers.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_text_based_editor.mdx | 2 +- api_docs/kbn_timerange.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_triggers_actions_ui_types.mdx | 2 +- api_docs/kbn_try_in_console.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_unified_data_table.mdx | 2 +- api_docs/kbn_unified_doc_viewer.mdx | 2 +- api_docs/kbn_unified_field_list.mdx | 2 +- api_docs/kbn_unsaved_changes_badge.mdx | 2 +- api_docs/kbn_use_tracked_promise.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_visualization_ui_components.mdx | 2 +- api_docs/kbn_visualization_utils.mdx | 2 +- api_docs/kbn_xstate_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kbn_zod_helpers.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/links.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/logs_data_access.mdx | 2 +- api_docs/logs_explorer.mdx | 2 +- api_docs/logs_shared.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/metrics_data_access.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/mock_idp_plugin.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/no_data_page.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/observability_a_i_assistant.mdx | 2 +- api_docs/observability_a_i_assistant_app.mdx | 2 +- .../observability_ai_assistant_management.mdx | 2 +- api_docs/observability_logs_explorer.mdx | 2 +- api_docs/observability_onboarding.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/painless_lab.mdx | 2 +- api_docs/plugin_directory.mdx | 14 +- api_docs/presentation_panel.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/profiling_data_access.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/search_connectors.mdx | 2 +- api_docs/search_notebooks.mdx | 2 +- api_docs/search_playground.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/security_solution_ess.mdx | 2 +- api_docs/security_solution_serverless.mdx | 2 +- api_docs/serverless.mdx | 2 +- api_docs/serverless_observability.mdx | 2 +- api_docs/serverless_search.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/slo.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.devdocs.json | 50 +++- api_docs/spaces.mdx | 4 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/text_based_languages.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_doc_viewer.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/uptime.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 700 files changed, 1106 insertions(+), 716 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 007fa5b67a6fa..daeb42ffb8d58 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 9e58f3b8d8535..fdaf5f3011e4f 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/ai_assistant_management_selection.mdx b/api_docs/ai_assistant_management_selection.mdx index c45287e0b0658..8ee59f1660a8e 100644 --- a/api_docs/ai_assistant_management_selection.mdx +++ b/api_docs/ai_assistant_management_selection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiAssistantManagementSelection title: "aiAssistantManagementSelection" image: https://source.unsplash.com/400x175/?github description: API docs for the aiAssistantManagementSelection plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiAssistantManagementSelection'] --- import aiAssistantManagementSelectionObj from './ai_assistant_management_selection.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 73450a7f78f8c..e16bcb06ce6a8 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index fda6ef4f66870..f81d03f4e38b4 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index a7527141ff849..9969eb16c4933 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index f815b118d4a1d..6cd6b6cd82030 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index 922f389398745..187a0f5e94e2c 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/assets_data_access.mdx b/api_docs/assets_data_access.mdx index 678a225953251..4ac7c1efeda96 100644 --- a/api_docs/assets_data_access.mdx +++ b/api_docs/assets_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetsDataAccess title: "assetsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the assetsDataAccess plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetsDataAccess'] --- import assetsDataAccessObj from './assets_data_access.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index cf225ce504bb5..da372079f04b6 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 6bc884d785244..bdb651e3ec8e6 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 2339e6488e252..5b85e6b575e91 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index e4365deef8c86..4f697e455f24a 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index adcfdad03b535..18124d3cb5965 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index c5c1b875ab500..fc457027fd24b 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index bd35cbf44cb50..844666847754a 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index fee53fa5b9556..7f5a2bf255ecf 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index 72778b64abe5f..ada95e37c760e 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 037be50397b62..52e3785ca0082 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 599d7a877df57..a0b6f6c745f98 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index d12a9000ac657..24806e0ace83b 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 24494121d6288..7ca2b6859aa0f 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 371d7024c9f99..f3b1432498c4d 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 99879301b021a..6099984cecdcf 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 43639daee3862..de74daf58a464 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.devdocs.json b/api_docs/data.devdocs.json index 35b158fdbb268..1a829a4a4fa1e 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -7117,7 +7117,9 @@ "label": "createFiltersFromRangeSelectAction", "description": [], "signature": [ - "(event: RangeSelectDataContext) => Promise<", + "(event: ", + "RangeSelectDataContext", + ") => Promise<", { "pluginId": "@kbn/es-query", "scope": "common", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 19f24774ec3bd..ec0a176f64d9c 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index ba13c86b8fdbb..4f335417d3a7a 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index d3a56c12bd53c..21359a0e53942 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -31207,6 +31207,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "data", + "id": "def-common.ESQL_TABLE_TYPE", + "type": "string", + "tags": [], + "label": "ESQL_TABLE_TYPE", + "description": [], + "signature": [ + "\"es_ql\"" + ], + "path": "src/plugins/data/common/search/strategies/esql_search/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "data", "id": "def-common.EsqlExpressionFunctionDefinition", diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 1653379b77ea4..326a538acedb3 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3182 | 31 | 2575 | 23 | +| 3183 | 31 | 2576 | 24 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index faf669346403b..213bb209f2cb3 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index a7d95b6abb2dc..275fafbf5bdfe 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index a30c351e121df..2d43c36507613 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.devdocs.json b/api_docs/data_views.devdocs.json index bd233d83f29d4..7dc04f6ab4d02 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -13471,7 +13471,7 @@ }, { "plugin": "infra", - "path": "x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts" + "path": "x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts" } ] }, diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 28f607334b6b4..b99e1ea88a792 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 4c297d5fa47d7..bd2b3af6fc7e1 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index ad3c71b1799e5..8b3d2cb385cae 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index 723ac413ace29..99fa23782fdd0 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -59,8 +59,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | @kbn/monaco, securitySolution | - | | | fleet, cloudSecurityPosture, exploratoryView, osquery, synthetics | - | -| | visualizations, lens, controls, dashboard, maps, discover, infra, profiling, slo, links | - | | | actions, alerting | - | +| | visualizations, lens, controls, dashboard, maps, discover, infra, profiling, links | - | | | discover, @kbn/reporting-public | - | | | data, discover, imageEmbeddable, embeddable | - | | | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, home, savedObjects, unifiedSearch, visualizations, fileUpload, dashboardEnhanced, transform, dashboard, discover, dataVisualizer | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 6f4c1dea939ff..0fa092962a5c5 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -868,7 +868,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [use_data_view.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/hooks/use_data_view.test.ts#:~:text=title) | - | +| | [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [validation_errors.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/logs/settings/validation_errors.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [index_patterns.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/common/dependency_mocks/index_patterns.ts#:~:text=title), [use_waffle_filters.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/pages/metrics/inventory_view/hooks/use_waffle_filters.test.ts#:~:text=title) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/public/plugin.ts#:~:text=registerEmbeddableFactory) | - | | | [saved_object_type.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/infra/server/lib/sources/saved_object_type.ts#:~:text=migrations) | - | @@ -1298,7 +1298,6 @@ migrates to using the Kibana Privilege model: https://github.com/elastic/kibana/ | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| | | [executor.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/server/lib/rules/slo_burn_rate/executor.test.ts#:~:text=alertFactory) | - | -| | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/public/plugin.ts#:~:text=registerEmbeddableFactory) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [slo.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability_solution/slo/server/saved_objects/slo.ts#:~:text=migrations) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index edf72c51f521e..0adc251e251d7 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 37967262f1944..f68bddf9e2aba 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index d44a5dc8a5e22..911255b7e718d 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 6339fb3b2b01b..09ad2c2d566fd 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/discover_shared.mdx b/api_docs/discover_shared.mdx index c66ec0daf4431..8a8cfacafa28e 100644 --- a/api_docs/discover_shared.mdx +++ b/api_docs/discover_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverShared title: "discoverShared" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverShared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverShared'] --- import discoverSharedObj from './discover_shared.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 9e970566e7c12..7e8e79976b702 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 7316bdb6b5955..15dea304c8862 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index 3398442983e06..3cf0bf3848d46 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -14230,10 +14230,6 @@ "plugin": "profiling", "path": "x-pack/plugins/observability_solution/profiling/public/embeddables/register_embeddables.ts" }, - { - "plugin": "slo", - "path": "x-pack/plugins/observability_solution/slo/public/plugin.ts" - }, { "plugin": "links", "path": "src/plugins/links/public/plugin.ts" diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 7ed91529a9fb8..62ccdd9cf539e 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 7a17f34c192a6..9d2381b6769e6 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index f38506783d091..250c25cc0abb6 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index b53756b6008e7..163cc5abe6205 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 5626378a0f6c0..6b40067c667d8 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 0cf2f1ba5c272..7d33fd6020d27 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index db7c297385ae5..0f6d5d3471041 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index a2de0179deafa..5dc6b884633ec 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index a36d3eeacfda9..f53165a935ec5 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index c9232260fef76..c95134c8a7a11 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 34d07a2d17dd4..9f4f57b65eb88 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 830c5a2876a8d..856552e924055 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 7b85d1bef2ab4..2b3488863c77f 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index e3801b2775ef3..66f20b9b4aac8 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 8094001ef9db8..08912e91fb9e1 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 8470505020506..181e48eb1a05a 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 88f1d1fbfe2ab..991fc65118080 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index a5f3ff34bfd8d..294a89e433a48 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index 599b36ce47d96..cbfeaf6c72087 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index be3151b23af8e..18cab88d815d5 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 59504e88c143c..035a5a352481f 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 830cc2d6d3bac..c0863b8f885ef 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index a7fdfd213a205..8e308858e0bdd 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 5fe0cba31cc51..83abd100f11e0 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 76e4c37f5e6f2..c7462f2303811 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 55e2afac5e1c5..4ee2103f27129 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index cb861226dee24..e89a6a5372912 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index 1597e582d0cbb..c0b57217e0dfb 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index ab89a00834c7a..bf40464fae816 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index b6fa38c321d9d..f996b5d207c4b 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index 75e493c31a6dc..292fb5a4fb188 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index ea1e067210f4b..7f9f2fce5c889 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 8e56a1eb40b8c..c14e7ceddfe0d 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 9def7c2abdc12..83850b9774664 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.devdocs.json b/api_docs/index_management.devdocs.json index 3d9a1f8659c81..9af93b2449330 100644 --- a/api_docs/index_management.devdocs.json +++ b/api_docs/index_management.devdocs.json @@ -1028,7 +1028,7 @@ "label": "IndexManagementConfig", "description": [], "signature": [ - "{ readonly ui: Readonly<{} & { enabled: boolean; }>; readonly enableIndexActions: boolean; readonly enableLegacyTemplates: boolean; readonly dev: Readonly<{} & { enableIndexDetailsPage: boolean; }>; readonly enableIndexStats: boolean; readonly editableIndexSettings: \"all\" | \"limited\"; readonly enableDataStreamsStorageColumn: boolean; readonly enableMappingsSourceFieldSection: boolean; readonly enableTogglingDataRetention: boolean; }" + "{ readonly ui: Readonly<{} & { enabled: boolean; }>; readonly enableIndexActions: boolean; readonly enableLegacyTemplates: boolean; readonly dev: Readonly<{} & { enableIndexDetailsPage: boolean; enableSemanticText: boolean; }>; readonly enableIndexStats: boolean; readonly editableIndexSettings: \"all\" | \"limited\"; readonly enableDataStreamsStorageColumn: boolean; readonly enableMappingsSourceFieldSection: boolean; readonly enableTogglingDataRetention: boolean; }" ], "path": "x-pack/plugins/index_management/server/config.ts", "deprecated": false, diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index c164b68269a3a..84ff0c682f117 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 8179231734ac8..1e8557c03a06a 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/ingest_pipelines.mdx b/api_docs/ingest_pipelines.mdx index 16d21094deb97..2a751eabf476d 100644 --- a/api_docs/ingest_pipelines.mdx +++ b/api_docs/ingest_pipelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ingestPipelines title: "ingestPipelines" image: https://source.unsplash.com/400x175/?github description: API docs for the ingestPipelines plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ingestPipelines'] --- import ingestPipelinesObj from './ingest_pipelines.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index e9d552e3f9ea2..931e8521f548d 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index a35fd8f1b2cee..7e3051789c0df 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 7090c08d86c7a..a2646cceedd7c 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_actions_types.mdx b/api_docs/kbn_actions_types.mdx index 01e426156b2c5..004249188e844 100644 --- a/api_docs/kbn_actions_types.mdx +++ b/api_docs/kbn_actions_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-actions-types title: "@kbn/actions-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/actions-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/actions-types'] --- import kbnActionsTypesObj from './kbn_actions_types.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 18e5c632f1bef..1b350134ed297 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_pattern_analysis.mdx b/api_docs/kbn_aiops_log_pattern_analysis.mdx index 4c5b01052f3b6..8368d39b7c953 100644 --- a/api_docs/kbn_aiops_log_pattern_analysis.mdx +++ b/api_docs/kbn_aiops_log_pattern_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-pattern-analysis title: "@kbn/aiops-log-pattern-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-pattern-analysis plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-pattern-analysis'] --- import kbnAiopsLogPatternAnalysisObj from './kbn_aiops_log_pattern_analysis.devdocs.json'; diff --git a/api_docs/kbn_aiops_log_rate_analysis.mdx b/api_docs/kbn_aiops_log_rate_analysis.mdx index 4cc6d2ad7ea26..59a7dd3107edf 100644 --- a/api_docs/kbn_aiops_log_rate_analysis.mdx +++ b/api_docs/kbn_aiops_log_rate_analysis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-log-rate-analysis title: "@kbn/aiops-log-rate-analysis" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-log-rate-analysis plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-log-rate-analysis'] --- import kbnAiopsLogRateAnalysisObj from './kbn_aiops_log_rate_analysis.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 2a27a5c94ea9b..084fcfc7cc5dc 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index d5e350b203769..40ace1fbfcff1 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerting_types.mdx b/api_docs/kbn_alerting_types.mdx index 4ae44b98ad151..0539e3bdc6107 100644 --- a/api_docs/kbn_alerting_types.mdx +++ b/api_docs/kbn_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-types title: "@kbn/alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 962877f5cb324..99c561f8a5cc7 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 6dadceb702c09..21994580434aa 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 6e637c7fd498f..23e2c6991e14d 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 6777b705fb00c..bb88a71b7b4a2 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_collection_utils.mdx b/api_docs/kbn_analytics_collection_utils.mdx index 82de6cf28a316..1d8949a3e3ad8 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-collection-utils'] --- import kbnAnalyticsCollectionUtilsObj from './kbn_analytics_collection_utils.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 17d590e5edcc6..bc753f800aca2 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index c8d3cf6238eb3..d47ede91e337c 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 146c5d74dbcb9..95066521fc445 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 8d56793fdf3ed..d282782705533 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 2bf4553c8aaa3..ec1736f806c53 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_data_view.mdx b/api_docs/kbn_apm_data_view.mdx index 65fa823226173..d0274e44fa47e 100644 --- a/api_docs/kbn_apm_data_view.mdx +++ b/api_docs/kbn_apm_data_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-data-view title: "@kbn/apm-data-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-data-view plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-data-view'] --- import kbnApmDataViewObj from './kbn_apm_data_view.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 83a1036e3a31e..e87f5a6c618ff 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 8e28aa8b3bd8f..c7ac35f36f2a9 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index bc3c68982fe36..336bb94e70088 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index cd92084b37a21..0814350c88ae1 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx index 1a17b58820b19..2d9cc4c7326fd 100644 --- a/api_docs/kbn_bfetch_error.mdx +++ b/api_docs/kbn_bfetch_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-bfetch-error title: "@kbn/bfetch-error" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/bfetch-error plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] --- import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 9ceb4537545fd..b6c21ca42f595 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-auto'] --- import kbnCalculateAutoObj from './kbn_calculate_auto.devdocs.json'; diff --git a/api_docs/kbn_calculate_width_from_char_count.mdx b/api_docs/kbn_calculate_width_from_char_count.mdx index 2da21884a39c9..34c7e1cc991b1 100644 --- a/api_docs/kbn_calculate_width_from_char_count.mdx +++ b/api_docs/kbn_calculate_width_from_char_count.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-width-from-char-count title: "@kbn/calculate-width-from-char-count" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-width-from-char-count plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/calculate-width-from-char-count'] --- import kbnCalculateWidthFromCharCountObj from './kbn_calculate_width_from_char_count.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index 1f8628c10a066..55e675e254720 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index c96953e6b0477..22419c6b2741a 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index b9334e11202ae..9455324ef98c5 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index 25ef89ab2129e..6a6886844a72d 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 472468d9658ec..cf8a6d5b53735 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index 96420b091b3d6..74d1fe06f1a5d 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index dbed3a1595621..ddf4930867e47 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index ba7c61490ea79..bd25b2997d6fa 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 98e11d4734e82..790106ba17a5d 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mock.mdx b/api_docs/kbn_code_editor_mock.mdx index 79ac063016227..8d01f73edac67 100644 --- a/api_docs/kbn_code_editor_mock.mdx +++ b/api_docs/kbn_code_editor_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mock title: "@kbn/code-editor-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mock plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mock'] --- import kbnCodeEditorMockObj from './kbn_code_editor_mock.devdocs.json'; diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx index ee8135c3c5272..437c8e8727c79 100644 --- a/api_docs/kbn_code_owners.mdx +++ b/api_docs/kbn_code_owners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-owners title: "@kbn/code-owners" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-owners plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] --- import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index cbc6b269aadf6..19eefa7283dd3 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index bf17c59ff2f8c..5f24a4035fb8a 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index f278a2cd42c42..b41898dfdd0f6 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index da9c328b06eb5..7b911342b7f3a 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 6499deac7ed4d..7f2467d11f11d 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index 599e54a370d65..5a53605ee86f7 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 37c3c5df8f407..3b993b0e92739 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_common.mdx b/api_docs/kbn_content_management_table_list_view_common.mdx index 1b1f74bb8ccbf..6a8c0d81a4a03 100644 --- a/api_docs/kbn_content_management_table_list_view_common.mdx +++ b/api_docs/kbn_content_management_table_list_view_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-common title: "@kbn/content-management-table-list-view-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-common'] --- import kbnContentManagementTableListViewCommonObj from './kbn_content_management_table_list_view_common.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 47def5616d9be..77738a474bcef 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index f829fa0be1685..b832326bd02cc 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index a0fac27d2f86e..5f75b948020b7 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 544cbe863444c..09cc4010d12ea 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 907fc10aa4b4b..b63fc93b513dc 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index cbde5dd789b59..026a76f2e12f8 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index bb3f83eec6d07..fe9110e11406e 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index f64d59412936c..ff52b7b28f84c 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index 6d621b5d04e62..4730af575894b 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index 00374fe6690e3..584c9e5078dd5 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 1152589c3b86e..fb7b24aad67de 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 6c909437eebb9..58ec240c0149e 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 001ab6bf40bea..ebeb47384bd3f 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 084abac29ca1e..d9396c9415d84 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index fdd05785ce775..5b02120cebfca 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 9d358ca5f2de9..ac8b2ecdb8688 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index f36433d2e9c5b..577f660827c50 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 0e57ff263dbae..400ffc727f1cf 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 20f5a40418b0f..774ecae2af60b 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index c261d79bf2d73..3ca608c2225c6 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index 838e3f663da1b..77805221829b8 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 38ea045ca2dd7..0482ca855b848 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 1816c02361a22..cd7f9abe0542e 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index 621c355a141bc..91dc7ffdd0a2d 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index b6d418c2e0628..356e704a4d460 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 1042ef8b2c958..036afa23222c9 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index 0d71d0d363465..ed2a55f16bd9f 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 233a7fd8c191f..089b6e2a37e53 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index e05dfb9341922..3f71a1789c7de 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index b763a6279b2f1..1d1e99115f551 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 080c0eb9bca6b..bddf8998e7055 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 5f58987c9ccd0..c35579dbd0bad 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index cc6176030089a..ed4a6a2c4c679 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 99363101902db..a1ee7844ff611 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index a467363ab63bd..2ca1da3c9cfaf 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index 24497190a6ff5..72041fa258b46 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 97c2849499dcc..38266ecec943d 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index 7d1c1bf0b3ec5..33f86b334c312 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index 4d42e35533dc6..41dca57ab3dd7 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index 1a7175df0ce14..d8442a823ee14 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 233713edd0555..8ca2bfa3fa892 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 27a78aa4ea953..efc3e1da582ab 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 377faefc70428..3ecd7bbf62be5 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index d42df4d9dccba..2214efedcead9 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 42ffc6f13c3fa..9bcd713d9769c 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index f1cc9527856c1..18583e84b540d 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index ad7935401bdb0..f27d2cce6ee6a 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 6ac69dc82b5d3..66ca34344ffab 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index d6346e04bbca5..b6c99c21a320f 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index d5f16431b874f..020360574945e 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 7b76f87033aa1..19fa71c2733a9 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 48bcc1ca9dc0a..486e7f4cca038 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 082a4a71f60dc..86165bc425499 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index be5564af87cad..200d5fc5d45e8 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 114a0ff780a59..8f6d68c35189a 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index c93b9631ea139..ae0cec5d3f86f 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index cb4d4f7c2a654..43fc1be850f3e 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index 6803d420afeaf..7228c249d4a46 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index c0b42689bd572..b0ba544f6e194 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index ce30ce160f9a9..486cfb0e8f1af 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index b2fe36ef699da..bfa3397f9a2dc 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index 8cc7c20f0f3c0..875895d2299ac 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 89e7c0186b20c..a5429ec68afd6 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index edb2a003f0a2b..05df20edc56bd 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 18d8da8245fb1..d2982d4c3901a 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index abe998d23c7aa..7355834d0e727 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index 0aea1e7cf2b3f..5368a9700a81a 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index fb135cc13c0ed..9d1725b174cc4 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index f72961f474669..4228467d98985 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 4be364df62491..c51a84f1de3a9 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index dfe6bea635234..d44112a7eb83c 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 9fc740f7ac77e..7413ae637f1a9 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 1f35eccd8de76..bea20d7b5a725 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 98e8799a39787..44da3921a64ef 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index 5b6437a31ff80..e68c3344b3b77 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 6042fac5ab04d..d44c41d9c8fd2 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 99d39ebbb6cc2..760b6912c4f6c 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 0a4e804eb98e3..7b6f56426f64c 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index 71c0f27c536ad..e1998b0c19d27 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 92f83cd068196..8c22d9754d30b 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 58b385afb584e..07d3a30095b42 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index b5538a6417a82..73a227db7df26 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index c58bbf1350b6a..45f855b6eb93d 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 17804a2a2932b..836a79d2e1c85 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 7d7b40d66f4c8..617489c2c0337 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index 6d1b9dccbcf31..354e4d5df4f09 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index d00376abf75ca..481bc142f3f82 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 654836432d519..87ebf1669431e 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 79e704bb8aaad..46dd4ca1daacb 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 7b1e7de9325ff..74237bd9f16a4 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 595d0a2300507..3ed1f26b2059a 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index db670ecf777c9..0edb895212bd3 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index 93a8b10e916ca..e851940d172bc 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index c8c86bd76f134..72483fd9561aa 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 21fd745e9c761..62386c15dc18d 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index d22fb294f037e..26f71ec661e8c 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index 6f1fd98b3e2ea..6429958b1e5a4 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 9cdb53cc04ad0..8c727e936f4ef 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index b65b4175560d9..08c08d866a361 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 6231c464fe4cf..3a397081ac1aa 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index f656575427911..c72e1b30f7920 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index fe9148f900292..06120017fe312 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index d327bd9459b03..e5a76743fa8ce 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 6e73b6ca6fc11..8cb2b12ec673b 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index 362c8d8b94a8a..36a4dee062c23 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 4de1a39d34bc0..ab5d046018f0b 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index 34bcb40f6464a..70d81a6cc29c3 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index cc4dc32a67311..e7e9709514ffe 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index 63d1d05339ab9..0c29b412b3804 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 8dfa8e576931c..a725580f9e4aa 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 8a83a877097d1..aba8f07b22426 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index 7993fb3b05d27..4ff9e4a5580b0 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index fb16e9ae363c1..c3442c405e06b 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 4813d017b13d6..ea2be412fd269 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index 95ba9aef4d19b..5747b2dc802dd 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index 17524185a8bbf..8b6630274b334 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index c2189782caae9..750c6324a9f79 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index 3bd8c5863eb7c..211796a69dd49 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index b44d92e1e61d0..558cf3c666d60 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 9cf3f26c110ef..62f7a46948a88 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index 49a79449cc5d9..e5743a11f333f 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index dcb8eaeea2d8e..81cca043b96bd 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 512ab251f55cc..a94a8de143167 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 03055eead866e..76b0083ae60a7 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 62d897062a66e..7479fa6df20fb 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index 906bb1847b9ba..7e49dd726c6df 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index cfbd9d6c7f399..e4da98e458fbf 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 63dd24e48669c..ab10087757e45 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index 0381032011a6b..d2561f4d9458f 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 13cd3db1ca3e0..ad81357c9c44b 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index e8a146fabe03c..733a3c2270731 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index 34decc2c10999..c588bf735e921 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 7aa3bf7b7bd14..300a1e8f967dd 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index c37e1125563f1..5208c71b5864f 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index fc57e6f6f1267..82425597738f8 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser.mdx b/api_docs/kbn_core_security_browser.mdx index 9b2160767157d..276a54be6f6a8 100644 --- a/api_docs/kbn_core_security_browser.mdx +++ b/api_docs/kbn_core_security_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser title: "@kbn/core-security-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser'] --- import kbnCoreSecurityBrowserObj from './kbn_core_security_browser.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_internal.mdx b/api_docs/kbn_core_security_browser_internal.mdx index c9e5e31a97135..ed92aab474ba3 100644 --- a/api_docs/kbn_core_security_browser_internal.mdx +++ b/api_docs/kbn_core_security_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-internal title: "@kbn/core-security-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-internal'] --- import kbnCoreSecurityBrowserInternalObj from './kbn_core_security_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_browser_mocks.mdx b/api_docs/kbn_core_security_browser_mocks.mdx index 14f00d5d6fe50..fae89452a15f1 100644 --- a/api_docs/kbn_core_security_browser_mocks.mdx +++ b/api_docs/kbn_core_security_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-browser-mocks title: "@kbn/core-security-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-browser-mocks'] --- import kbnCoreSecurityBrowserMocksObj from './kbn_core_security_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_security_common.mdx b/api_docs/kbn_core_security_common.mdx index 525794f559e16..7a4b5723092af 100644 --- a/api_docs/kbn_core_security_common.mdx +++ b/api_docs/kbn_core_security_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-common title: "@kbn/core-security-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-common'] --- import kbnCoreSecurityCommonObj from './kbn_core_security_common.devdocs.json'; diff --git a/api_docs/kbn_core_security_server.mdx b/api_docs/kbn_core_security_server.mdx index 6d7ec3d7d8e93..3041aba870646 100644 --- a/api_docs/kbn_core_security_server.mdx +++ b/api_docs/kbn_core_security_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server title: "@kbn/core-security-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server'] --- import kbnCoreSecurityServerObj from './kbn_core_security_server.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_internal.mdx b/api_docs/kbn_core_security_server_internal.mdx index 94fd5961d50d7..1de5728402828 100644 --- a/api_docs/kbn_core_security_server_internal.mdx +++ b/api_docs/kbn_core_security_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-internal title: "@kbn/core-security-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-internal'] --- import kbnCoreSecurityServerInternalObj from './kbn_core_security_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_security_server_mocks.mdx b/api_docs/kbn_core_security_server_mocks.mdx index d63d58a224dbf..84fff0b44e96b 100644 --- a/api_docs/kbn_core_security_server_mocks.mdx +++ b/api_docs/kbn_core_security_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-security-server-mocks title: "@kbn/core-security-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-security-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-security-server-mocks'] --- import kbnCoreSecurityServerMocksObj from './kbn_core_security_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index d5131ea030434..d59f43b8b7dde 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index f3332cc7f9f30..00a434c7bcfec 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index 91f95b8105712..e85d4ff8dddcd 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 334788c03fcd7..c3614605f87d3 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index 3c411e4e0c948..312ddbe7e6a7e 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 361d6f8deeea6..79dfb4b2b5fc2 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index b932d4dbd6bf0..c796585c7a040 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index 3c19c0723ec4f..d1bd261befd66 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index f4ae005f6a14a..accdf730d79ba 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index e2483e1a446e1..624939b00737c 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index b3b3d6444879d..534dd53350057 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 6534e027bbac2..5d1c2292454ef 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index 30a45d3fa1d43..cf426aa0b6f98 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index cdcdfd085da8b..1ecb4f03ec781 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index 263cc553262cc..14f69f89a6479 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 0be6298718c6f..b358e8b3556fc 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index a3ae704f58fdd..73740388f54c0 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 008865cf75451..86913e6d307f8 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 8d8394ec3d41a..7282231e16bc2 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index 9d4eb0002ef39..d837aed57e42d 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index 73178642f1051..062d7d9db798d 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 67fd2a9a938e3..85a91a241d5a4 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 48980ac73b4e2..ef17edf87d83c 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser.mdx b/api_docs/kbn_core_user_profile_browser.mdx index cc0b7d081ffae..3c4ba2b2ec6ce 100644 --- a/api_docs/kbn_core_user_profile_browser.mdx +++ b/api_docs/kbn_core_user_profile_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser title: "@kbn/core-user-profile-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser'] --- import kbnCoreUserProfileBrowserObj from './kbn_core_user_profile_browser.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_internal.mdx b/api_docs/kbn_core_user_profile_browser_internal.mdx index 0b89752421732..c4b23ae565b5b 100644 --- a/api_docs/kbn_core_user_profile_browser_internal.mdx +++ b/api_docs/kbn_core_user_profile_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-internal title: "@kbn/core-user-profile-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-internal'] --- import kbnCoreUserProfileBrowserInternalObj from './kbn_core_user_profile_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_browser_mocks.mdx b/api_docs/kbn_core_user_profile_browser_mocks.mdx index ae8b77ad7b193..891d867958c15 100644 --- a/api_docs/kbn_core_user_profile_browser_mocks.mdx +++ b/api_docs/kbn_core_user_profile_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-browser-mocks title: "@kbn/core-user-profile-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-browser-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-browser-mocks'] --- import kbnCoreUserProfileBrowserMocksObj from './kbn_core_user_profile_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_common.mdx b/api_docs/kbn_core_user_profile_common.mdx index 657f320c1100b..57f3416f9d8c6 100644 --- a/api_docs/kbn_core_user_profile_common.mdx +++ b/api_docs/kbn_core_user_profile_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-common title: "@kbn/core-user-profile-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-common'] --- import kbnCoreUserProfileCommonObj from './kbn_core_user_profile_common.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server.mdx b/api_docs/kbn_core_user_profile_server.mdx index b5b6f57f488f8..501d186f61c2d 100644 --- a/api_docs/kbn_core_user_profile_server.mdx +++ b/api_docs/kbn_core_user_profile_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server title: "@kbn/core-user-profile-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server'] --- import kbnCoreUserProfileServerObj from './kbn_core_user_profile_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_internal.mdx b/api_docs/kbn_core_user_profile_server_internal.mdx index 1b76a1eb3d879..b9699e9a70bec 100644 --- a/api_docs/kbn_core_user_profile_server_internal.mdx +++ b/api_docs/kbn_core_user_profile_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-internal title: "@kbn/core-user-profile-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-internal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-internal'] --- import kbnCoreUserProfileServerInternalObj from './kbn_core_user_profile_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_profile_server_mocks.mdx b/api_docs/kbn_core_user_profile_server_mocks.mdx index 399c457fd4787..013da69ed415c 100644 --- a/api_docs/kbn_core_user_profile_server_mocks.mdx +++ b/api_docs/kbn_core_user_profile_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-profile-server-mocks title: "@kbn/core-user-profile-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-profile-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-profile-server-mocks'] --- import kbnCoreUserProfileServerMocksObj from './kbn_core_user_profile_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 38787ae7fa0d5..4eaf318613874 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index f539eee5f78a9..91e4381580aef 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index 1556e68abb099..ce8e5d0f8b22d 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 4a47c0ef80838..52695b01d6726 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_icons.mdx b/api_docs/kbn_custom_icons.mdx index dff29f6b0f077..14cf623f7c90f 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-icons'] --- import kbnCustomIconsObj from './kbn_custom_icons.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index c7a94ef3dda45..77183dcdda0e5 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 69c0f2528adce..51392e5d1c734 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_forge.mdx b/api_docs/kbn_data_forge.mdx index 94e0841b320f3..92027123e1072 100644 --- a/api_docs/kbn_data_forge.mdx +++ b/api_docs/kbn_data_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-forge title: "@kbn/data-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-forge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-forge'] --- import kbnDataForgeObj from './kbn_data_forge.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 2215254526bcb..8d4310ca9e4b0 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_data_stream_adapter.mdx b/api_docs/kbn_data_stream_adapter.mdx index 51eabe740c3c1..b3f7ac6927440 100644 --- a/api_docs/kbn_data_stream_adapter.mdx +++ b/api_docs/kbn_data_stream_adapter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-stream-adapter title: "@kbn/data-stream-adapter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-stream-adapter plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-stream-adapter'] --- import kbnDataStreamAdapterObj from './kbn_data_stream_adapter.devdocs.json'; diff --git a/api_docs/kbn_data_view_utils.mdx b/api_docs/kbn_data_view_utils.mdx index 1b61b79dccc93..30a0e9f4f6c9f 100644 --- a/api_docs/kbn_data_view_utils.mdx +++ b/api_docs/kbn_data_view_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-view-utils title: "@kbn/data-view-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-view-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-view-utils'] --- import kbnDataViewUtilsObj from './kbn_data_view_utils.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index a777b53158e49..dd2d7694d16b6 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index d17394158986b..18a4f625e0b5d 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index 949fa99745ca9..b41717cb7df4d 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_fleet.mdx b/api_docs/kbn_deeplinks_fleet.mdx index eb1fe118432ed..e1f13f7b68744 100644 --- a/api_docs/kbn_deeplinks_fleet.mdx +++ b/api_docs/kbn_deeplinks_fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-fleet title: "@kbn/deeplinks-fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-fleet plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-fleet'] --- import kbnDeeplinksFleetObj from './kbn_deeplinks_fleet.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index 64d6ed9f1f825..0465ac5bc90fe 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index ad4dda37aabf7..80097e64233d9 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index b85bb3ca1b665..4e8bf129e445d 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 82a96981eb133..8d2d195bbc345 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_security.mdx b/api_docs/kbn_deeplinks_security.mdx index cc17543cba74c..c069d4bab551d 100644 --- a/api_docs/kbn_deeplinks_security.mdx +++ b/api_docs/kbn_deeplinks_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-security title: "@kbn/deeplinks-security" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-security plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-security'] --- import kbnDeeplinksSecurityObj from './kbn_deeplinks_security.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_shared.mdx b/api_docs/kbn_deeplinks_shared.mdx index 6347f7ff9d568..8d7b97be64fb8 100644 --- a/api_docs/kbn_deeplinks_shared.mdx +++ b/api_docs/kbn_deeplinks_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-shared title: "@kbn/deeplinks-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-shared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-shared'] --- import kbnDeeplinksSharedObj from './kbn_deeplinks_shared.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index 7156db6a7d083..7bbbd0c040f92 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index c4f21a9ae9e37..552d71068622c 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index 799125d79f534..a2e92aca73c5f 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index 9ac2897d8114b..d877823e71a79 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 3bfb374499fb1..ae5cc67c6cdff 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index b89028775a2af..2ae92aff98ae6 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index e0130fdbc9770..db533b9a6762c 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index ca3c40d95674b..60723e1b421ca 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index 4d7f26b85b885..27defba60fdd9 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 36209aad520e3..4bf2d4caf3e85 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 1884280fea8a3..a0700881d4663 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 0ef8e1895ac02..901c42c21c1f3 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index 6eaebcae78676..f4805901fd899 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index ed87b9eba83d8..378b0bcb7b216 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index dfeaf1e1bd0aa..4fb3e7c0f2a8a 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index 5781af4f1bfb0..e537e340c02cf 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx index 8ae742997ea71..e183824b4c06d 100644 --- a/api_docs/kbn_elastic_assistant_common.mdx +++ b/api_docs/kbn_elastic_assistant_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant-common title: "@kbn/elastic-assistant-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] --- import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; diff --git a/api_docs/kbn_entities_schema.mdx b/api_docs/kbn_entities_schema.mdx index faf0d90f30b04..7e2d8b6ac00e0 100644 --- a/api_docs/kbn_entities_schema.mdx +++ b/api_docs/kbn_entities_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-entities-schema title: "@kbn/entities-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/entities-schema plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/entities-schema'] --- import kbnEntitiesSchemaObj from './kbn_entities_schema.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index fbd0fa95ea454..3e8d3cb9299f4 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index e8dcd5e568b00..0e51fdfc5d042 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index f6b14cea32796..2ee1eb43700f7 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 9fdc2b1161d0e..621025f12b7ee 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 91572c451be33..21d0720b6d29a 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index a6fc22f62d479..01aa818219813 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_esql_ast.mdx b/api_docs/kbn_esql_ast.mdx index 870a7738e7f9c..41542b91b8669 100644 --- a/api_docs/kbn_esql_ast.mdx +++ b/api_docs/kbn_esql_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-ast title: "@kbn/esql-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-ast plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-ast'] --- import kbnEsqlAstObj from './kbn_esql_ast.devdocs.json'; diff --git a/api_docs/kbn_esql_utils.devdocs.json b/api_docs/kbn_esql_utils.devdocs.json index fc0f2cc69906f..c24e9c4eea755 100644 --- a/api_docs/kbn_esql_utils.devdocs.json +++ b/api_docs/kbn_esql_utils.devdocs.json @@ -1000,6 +1000,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/esql-utils", + "id": "def-common.hasTransformationalCommand", + "type": "Function", + "tags": [], + "label": "hasTransformationalCommand", + "description": [], + "signature": [ + "(esql: string | undefined) => boolean" + ], + "path": "packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/esql-utils", + "id": "def-common.hasTransformationalCommand.$1", + "type": "string", + "tags": [], + "label": "esql", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-esql-utils/src/utils/query_parsing_helpers.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/esql-utils", "id": "def-common.removeDropCommandsFromESQLQuery", diff --git a/api_docs/kbn_esql_utils.mdx b/api_docs/kbn_esql_utils.mdx index 578cc2faecd12..62d93f8c93215 100644 --- a/api_docs/kbn_esql_utils.mdx +++ b/api_docs/kbn_esql_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-utils title: "@kbn/esql-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-utils'] --- import kbnEsqlUtilsObj from './kbn_esql_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 52 | 0 | 50 | 0 | +| 54 | 0 | 52 | 0 | ## Common diff --git a/api_docs/kbn_esql_validation_autocomplete.mdx b/api_docs/kbn_esql_validation_autocomplete.mdx index 49458cdc03db0..9507226681669 100644 --- a/api_docs/kbn_esql_validation_autocomplete.mdx +++ b/api_docs/kbn_esql_validation_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-esql-validation-autocomplete title: "@kbn/esql-validation-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/esql-validation-autocomplete plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/esql-validation-autocomplete'] --- import kbnEsqlValidationAutocompleteObj from './kbn_esql_validation_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index e6baa12440493..9c5745d3550a9 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index e57a5ecb345e0..d0bae04c9cf91 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 6692ba53f6458..eadab089c5db7 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 08c8e98066845..ba2bb100fc381 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index 872156c797e8c..c525a3fad9218 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index 588c36c87332d..f257bf3c6e8f4 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_formatters.mdx b/api_docs/kbn_formatters.mdx index c86e56de64496..0d4de92d2c6a0 100644 --- a/api_docs/kbn_formatters.mdx +++ b/api_docs/kbn_formatters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-formatters title: "@kbn/formatters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/formatters plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/formatters'] --- import kbnFormattersObj from './kbn_formatters.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 559d47c01a7de..fb0b87445c39c 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx index f422587ecc2dc..01f3f1bce121e 100644 --- a/api_docs/kbn_ftr_common_functional_ui_services.mdx +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-ui-services title: "@kbn/ftr-common-functional-ui-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-ui-services plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] --- import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 10b2985621e19..66987ba2c4913 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index 6c81ab9dec28d..098386387dfb3 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 416f4991ad09f..e074d3526abbf 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index aa8deafe55c1c..39269521bead3 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 213757d025450..47a42b033220e 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index f26f2cecad027..c5a632de03c90 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index b3b2722cec2df..979db0202f641 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 61f9840bfd9e0..c62d75358955b 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index 64859d6488416..0b7c5481122c8 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index ee6db5ed7dffd..6ca2efe6efed5 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 51c27347f4852..168538970bfe8 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 436c5777e9416..09396a77928cc 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_index_management.mdx b/api_docs/kbn_index_management.mdx index 7912dae2fdf46..0a5cec326a0fd 100644 --- a/api_docs/kbn_index_management.mdx +++ b/api_docs/kbn_index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-index-management title: "@kbn/index-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/index-management plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/index-management'] --- import kbnIndexManagementObj from './kbn_index_management.devdocs.json'; diff --git a/api_docs/kbn_inference_integration_flyout.mdx b/api_docs/kbn_inference_integration_flyout.mdx index 19caca0579a3a..43f35ee662a72 100644 --- a/api_docs/kbn_inference_integration_flyout.mdx +++ b/api_docs/kbn_inference_integration_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-inference_integration_flyout title: "@kbn/inference_integration_flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/inference_integration_flyout plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/inference_integration_flyout'] --- import kbnInferenceIntegrationFlyoutObj from './kbn_inference_integration_flyout.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index a1c174987e3b2..4a7cd386d6637 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 0ef231bee51a4..d228ba81e2ae7 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 452040267cebb..752154fe8205c 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_ipynb.mdx b/api_docs/kbn_ipynb.mdx index b71c9ff4837c0..4bc857789e035 100644 --- a/api_docs/kbn_ipynb.mdx +++ b/api_docs/kbn_ipynb.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ipynb title: "@kbn/ipynb" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ipynb plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ipynb'] --- import kbnIpynbObj from './kbn_ipynb.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index fa6e70ac1ed93..ab93efb6d4b80 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index f2cb48a3627b8..02465fea5b8b7 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 6742d26dcae45..8947cb452905c 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index 7ade04d52e4a8..1a59adb8e9b3b 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index ad5c3f6f37a40..e7f5a88f78fd2 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index 9ddcbf8ad3ae0..2ca1783bee27f 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_lens_formula_docs.mdx b/api_docs/kbn_lens_formula_docs.mdx index 2bc8efd61c186..2eb4ea4ab55eb 100644 --- a/api_docs/kbn_lens_formula_docs.mdx +++ b/api_docs/kbn_lens_formula_docs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-formula-docs title: "@kbn/lens-formula-docs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-formula-docs plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-formula-docs'] --- import kbnLensFormulaDocsObj from './kbn_lens_formula_docs.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index c16356e58d73e..3d9c2451779e1 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 45c54d6ce8f29..5a4b9876498a6 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_content_badge.mdx b/api_docs/kbn_managed_content_badge.mdx index bf584ad706562..186f54572c38e 100644 --- a/api_docs/kbn_managed_content_badge.mdx +++ b/api_docs/kbn_managed_content_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-content-badge title: "@kbn/managed-content-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-content-badge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-content-badge'] --- import kbnManagedContentBadgeObj from './kbn_managed_content_badge.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 54ecdda1db2bc..0932f1a8f0800 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 8919d0610f2d6..013d1d0bf7895 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index 18ba80d52a1d8..d145246b4dee2 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 6cdd3754eb575..2f5046db79835 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index f06b04c9b4e9f..1bd43ceccca11 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index 5182b52733766..c102a55ef852b 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index 1d1333c514069..98ac7da1bddc0 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index a6c57d2679c20..82a78cee848ee 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 1aa2558466b9a..11bc3a4c0cc8c 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 0216db77e2541..81d364b74a832 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 873cb21d87155..4d5fbb8638c09 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 6d095efbd14a9..dba78dea4bfa2 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index c82232ea18aed..1087219896694 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 6746e2168c713..fb2289ccd48a6 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index 00dc196b612a3..ec38be3deacdc 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 216c680d5bc92..e0097c5040bc4 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index 655addfc327e0..50d3ef624be75 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_cancellable_search.mdx b/api_docs/kbn_ml_cancellable_search.mdx index 9279be7cab54c..7ffccd15b65dc 100644 --- a/api_docs/kbn_ml_cancellable_search.mdx +++ b/api_docs/kbn_ml_cancellable_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-cancellable-search title: "@kbn/ml-cancellable-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-cancellable-search plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-cancellable-search'] --- import kbnMlCancellableSearchObj from './kbn_ml_cancellable_search.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 54e147e71173f..7bbdde8f7db69 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index ac644f28f9944..b39998c7d5834 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index 0e06489bcb017..226560e601c95 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index a64750bfcf07c..09936cd82ec03 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 815e2df99d117..7e5c5ddab80a8 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 3d8db9c9a8d25..7c13b84e018b2 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index b9314e19932e7..c0f79a672dd87 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index f588ddfe36832..4de94ca590b79 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index d29b329188f87..0b39a4fbd1ee6 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 57ea2a9b30780..41ce6a80ede59 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index 2af6d1e30429b..4da6ffe435fe2 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index ca9dc59936c5c..58b49f0a94dc9 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index bb78688e3f9ca..e7a5eb64915b2 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index b47e3f81b3942..d98f8bd6ec7c9 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 44e36bf9c1ab4..825ef52914353 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 8057880e30c40..886facc920c42 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index a75a4e96bf760..1d8f99122b69e 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index 9782e3719d0af..95873e82a1ed4 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index d1d695e8a2383..78f9af02cbc2d 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_time_buckets.mdx b/api_docs/kbn_ml_time_buckets.mdx index 00c99dfa99e31..f553774e2645a 100644 --- a/api_docs/kbn_ml_time_buckets.mdx +++ b/api_docs/kbn_ml_time_buckets.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-time-buckets title: "@kbn/ml-time-buckets" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-time-buckets plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-time-buckets'] --- import kbnMlTimeBucketsObj from './kbn_ml_time_buckets.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index d24c763ea11f5..9ed4df8f791ae 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 708f306c26a6b..5d26dbb64f097 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-ui-actions'] --- import kbnMlUiActionsObj from './kbn_ml_ui_actions.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 40a49a16d0e32..a8e5fe380d59c 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_mock_idp_utils.mdx b/api_docs/kbn_mock_idp_utils.mdx index 1210d4b177616..ee64b879984ff 100644 --- a/api_docs/kbn_mock_idp_utils.mdx +++ b/api_docs/kbn_mock_idp_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mock-idp-utils title: "@kbn/mock-idp-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mock-idp-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mock-idp-utils'] --- import kbnMockIdpUtilsObj from './kbn_mock_idp_utils.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index b2e615c757bec..932f7fe996baf 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 95c4b45e27b1c..10f6b366aef3b 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index edab6c2f8d45d..2205716119ebc 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 558863972f7f0..0b1db4d46c181 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx index dfd1bc0512ab0..bc45d1baf2012 100644 --- a/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx +++ b/api_docs/kbn_observability_get_padded_alert_time_range_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-get-padded-alert-time-range-util title: "@kbn/observability-get-padded-alert-time-range-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-get-padded-alert-time-range-util plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-get-padded-alert-time-range-util'] --- import kbnObservabilityGetPaddedAlertTimeRangeUtilObj from './kbn_observability_get_padded_alert_time_range_util.devdocs.json'; diff --git a/api_docs/kbn_openapi_bundler.mdx b/api_docs/kbn_openapi_bundler.mdx index b99156ac0b702..0be4fcadecdfe 100644 --- a/api_docs/kbn_openapi_bundler.mdx +++ b/api_docs/kbn_openapi_bundler.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-bundler title: "@kbn/openapi-bundler" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-bundler plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-bundler'] --- import kbnOpenapiBundlerObj from './kbn_openapi_bundler.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index f14af3c059fde..5b798fdcc851a 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 3002cf339f020..3873899e81b85 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index ac0621496a6f6..b78d88a45304e 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 5e97b299897ba..19d83df3206cd 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_panel_loader.mdx b/api_docs/kbn_panel_loader.mdx index e7209b93128f2..02fdcae82995b 100644 --- a/api_docs/kbn_panel_loader.mdx +++ b/api_docs/kbn_panel_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-panel-loader title: "@kbn/panel-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/panel-loader plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/panel-loader'] --- import kbnPanelLoaderObj from './kbn_panel_loader.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 0afee9083ab2d..d55a8db63d72d 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_check.mdx b/api_docs/kbn_plugin_check.mdx index 297974d343a7d..4c16dd2e4454a 100644 --- a/api_docs/kbn_plugin_check.mdx +++ b/api_docs/kbn_plugin_check.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-check title: "@kbn/plugin-check" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-check plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-check'] --- import kbnPluginCheckObj from './kbn_plugin_check.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 7485ace1eda6c..7fb02a3ab2fc9 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index ace45ffb489ae..56f51fca315d8 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_presentation_containers.mdx b/api_docs/kbn_presentation_containers.mdx index f5cf0dc3a1f21..8835a9b1ff312 100644 --- a/api_docs/kbn_presentation_containers.mdx +++ b/api_docs/kbn_presentation_containers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-containers title: "@kbn/presentation-containers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-containers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-containers'] --- import kbnPresentationContainersObj from './kbn_presentation_containers.devdocs.json'; diff --git a/api_docs/kbn_presentation_publishing.devdocs.json b/api_docs/kbn_presentation_publishing.devdocs.json index fdfac45fbefb3..93e1c1bf2fbf5 100644 --- a/api_docs/kbn_presentation_publishing.devdocs.json +++ b/api_docs/kbn_presentation_publishing.devdocs.json @@ -1984,6 +1984,46 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/presentation-publishing", + "id": "def-common.useFetchContext", + "type": "Function", + "tags": [], + "label": "useFetchContext", + "description": [], + "signature": [ + "(api: unknown) => ", + { + "pluginId": "@kbn/presentation-publishing", + "scope": "common", + "docId": "kibKbnPresentationPublishingPluginApi", + "section": "def-common.FetchContext", + "text": "FetchContext" + } + ], + "path": "packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/presentation-publishing", + "id": "def-common.useFetchContext.$1", + "type": "Unknown", + "tags": [], + "label": "api", + "description": [], + "signature": [ + "unknown" + ], + "path": "packages/presentation/presentation_publishing/interfaces/fetch/fetch.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/presentation-publishing", "id": "def-common.useInheritedViewMode", diff --git a/api_docs/kbn_presentation_publishing.mdx b/api_docs/kbn_presentation_publishing.mdx index ceebc7ac83580..b5f654891df18 100644 --- a/api_docs/kbn_presentation_publishing.mdx +++ b/api_docs/kbn_presentation_publishing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-presentation-publishing title: "@kbn/presentation-publishing" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/presentation-publishing plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/presentation-publishing'] --- import kbnPresentationPublishingObj from './kbn_presentation_publishing.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kib | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 202 | 0 | 167 | 5 | +| 204 | 0 | 169 | 5 | ## Common diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 956eb75e30be9..2ec65ce5dd62a 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 7ae20499de1fd..c3edf4d476f54 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 4b25ff900a1f1..da5d4347af4b7 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_hooks.mdx b/api_docs/kbn_react_hooks.mdx index 654acf461028e..b313d8f72e28a 100644 --- a/api_docs/kbn_react_hooks.mdx +++ b/api_docs/kbn_react_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-hooks title: "@kbn/react-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-hooks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-hooks'] --- import kbnReactHooksObj from './kbn_react_hooks.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index 6454ca4440b14..b0957fecad23c 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index f2cba0212429f..4ee18b1e8c925 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index a2b525b42bcd0..d49269d841aca 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 81e850e7c72df..15fce585e4bcc 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index c54a570c9ddfc..58524a5734a86 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 55af9cc7c5c47..8b129cc73a56f 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 9d06b02bebcbc..3dab8188138e3 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 434ca8a520038..ffbed8cc4603f 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 3d08bcb10c89d..834bbb2810a53 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 8a720c0082e0d..0e849a9675e76 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 8f1bd336fb910..b9502029408db 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_csv_share_panel.mdx b/api_docs/kbn_reporting_csv_share_panel.mdx index c8c78efb5c1e2..5be23d32f8bec 100644 --- a/api_docs/kbn_reporting_csv_share_panel.mdx +++ b/api_docs/kbn_reporting_csv_share_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-csv-share-panel title: "@kbn/reporting-csv-share-panel" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-csv-share-panel plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-csv-share-panel'] --- import kbnReportingCsvSharePanelObj from './kbn_reporting_csv_share_panel.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv.mdx b/api_docs/kbn_reporting_export_types_csv.mdx index cfa6d6da96cc8..2b137b778b582 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv'] --- import kbnReportingExportTypesCsvObj from './kbn_reporting_export_types_csv.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 2780da85adab0..56f2a62022d43 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index fb8a6d9e62425..9a3ce288a853f 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf'] --- import kbnReportingExportTypesPdfObj from './kbn_reporting_export_types_pdf.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_pdf_common.mdx b/api_docs/kbn_reporting_export_types_pdf_common.mdx index b4f8f381125c4..f79a4c72af08b 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-pdf-common'] --- import kbnReportingExportTypesPdfCommonObj from './kbn_reporting_export_types_pdf_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png.mdx b/api_docs/kbn_reporting_export_types_png.mdx index 9aa265a90fa85..3e7b011609170 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png'] --- import kbnReportingExportTypesPngObj from './kbn_reporting_export_types_png.devdocs.json'; diff --git a/api_docs/kbn_reporting_export_types_png_common.mdx b/api_docs/kbn_reporting_export_types_png_common.mdx index c203c52249a07..617030c9a9685 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-png-common'] --- import kbnReportingExportTypesPngCommonObj from './kbn_reporting_export_types_png_common.devdocs.json'; diff --git a/api_docs/kbn_reporting_mocks_server.mdx b/api_docs/kbn_reporting_mocks_server.mdx index dd72fd6ca1878..205c7006a61c2 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-mocks-server'] --- import kbnReportingMocksServerObj from './kbn_reporting_mocks_server.devdocs.json'; diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index 4cfbd615316be..de3a4d0abe846 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.devdocs.json'; diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index 7f42011d2aa0c..bc2ae6079a63c 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-server'] --- import kbnReportingServerObj from './kbn_reporting_server.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index 4a7981635308d..26ed562b942ae 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index 38346a4e39963..aa6b2ae552c61 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_to_openapispec.mdx b/api_docs/kbn_router_to_openapispec.mdx index 300d466e7eea2..970f72366b510 100644 --- a/api_docs/kbn_router_to_openapispec.mdx +++ b/api_docs/kbn_router_to_openapispec.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-to-openapispec title: "@kbn/router-to-openapispec" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-to-openapispec plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-to-openapispec'] --- import kbnRouterToOpenapispecObj from './kbn_router_to_openapispec.devdocs.json'; diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx index f5c6b3bb0ef50..88081a11c8f49 100644 --- a/api_docs/kbn_router_utils.mdx +++ b/api_docs/kbn_router_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-router-utils title: "@kbn/router-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/router-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] --- import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index 4feadb9dbcfbf..a7e15e74a8e96 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index a451e46369f7d..02f1610f9bf46 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 99ad978bb1f77..5c7bef31faa40 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index d15c94b3f9364..78c1554c3e9e5 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index f7e4549263202..669f5be7fa9c1 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -1121,7 +1121,15 @@ "section": "def-common.ElasticsearchClient", "text": "ElasticsearchClient" }, - ", connectorId?: string | undefined, from?: number, size?: number, syncJobType?: \"all\" | \"content\" | \"access_control\") => Promise<", + ", connectorId?: string | undefined, from?: number, size?: number, syncJobType?: \"all\" | \"content\" | \"access_control\", syncStatus?: ", + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.SyncStatus", + "text": "SyncStatus" + }, + " | undefined) => Promise<", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -1223,6 +1231,28 @@ "deprecated": false, "trackAdoption": false, "isRequired": true + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.fetchSyncJobs.$6", + "type": "CompoundType", + "tags": [], + "label": "syncStatus", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.SyncStatus", + "text": "SyncStatus" + }, + " | undefined" + ], + "path": "packages/kbn-search-connectors/lib/fetch_sync_jobs.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false } ], "returnComment": [], @@ -41186,6 +41216,206 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security", + "type": "Object", + "tags": [], + "label": "use_document_level_security", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.configuration.use_document_level_security.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] }, @@ -41267,6 +41497,33 @@ } ] }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "type": "Object", + "tags": [], + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.outlook.features.FeatureName.INCREMENTAL_SYNC", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index a98862d2203ef..e7a2591314e5a 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3672 | 0 | 3672 | 0 | +| 3689 | 0 | 3689 | 0 | ## Common diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx index c7821604daf34..04adeccc6f87e 100644 --- a/api_docs/kbn_search_errors.mdx +++ b/api_docs/kbn_search_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-errors title: "@kbn/search-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-errors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] --- import kbnSearchErrorsObj from './kbn_search_errors.devdocs.json'; diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index a028213130b8f..de2802d3fd1d6 100644 --- a/api_docs/kbn_search_index_documents.mdx +++ b/api_docs/kbn_search_index_documents.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-index-documents title: "@kbn/search-index-documents" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-index-documents plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-index-documents'] --- import kbnSearchIndexDocumentsObj from './kbn_search_index_documents.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index 021bcdbd3a39a..2eb135563f5b6 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_search_types.mdx b/api_docs/kbn_search_types.mdx index 718dec065a26e..8a9a615a1dc54 100644 --- a/api_docs/kbn_search_types.mdx +++ b/api_docs/kbn_search_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-types title: "@kbn/search-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-types'] --- import kbnSearchTypesObj from './kbn_search_types.devdocs.json'; diff --git a/api_docs/kbn_security_hardening.mdx b/api_docs/kbn_security_hardening.mdx index d6b640edf0ad7..403ecc0c20333 100644 --- a/api_docs/kbn_security_hardening.mdx +++ b/api_docs/kbn_security_hardening.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-hardening title: "@kbn/security-hardening" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-hardening plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-hardening'] --- import kbnSecurityHardeningObj from './kbn_security_hardening.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_common.mdx b/api_docs/kbn_security_plugin_types_common.mdx index fcbd593432e5f..35fd2c9bd6437 100644 --- a/api_docs/kbn_security_plugin_types_common.mdx +++ b/api_docs/kbn_security_plugin_types_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-common title: "@kbn/security-plugin-types-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-common plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-common'] --- import kbnSecurityPluginTypesCommonObj from './kbn_security_plugin_types_common.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_public.mdx b/api_docs/kbn_security_plugin_types_public.mdx index 9f97e1833832a..bb25f69c106eb 100644 --- a/api_docs/kbn_security_plugin_types_public.mdx +++ b/api_docs/kbn_security_plugin_types_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-public title: "@kbn/security-plugin-types-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-public plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-public'] --- import kbnSecurityPluginTypesPublicObj from './kbn_security_plugin_types_public.devdocs.json'; diff --git a/api_docs/kbn_security_plugin_types_server.mdx b/api_docs/kbn_security_plugin_types_server.mdx index dcf87716624a1..d10f6fd5ab460 100644 --- a/api_docs/kbn_security_plugin_types_server.mdx +++ b/api_docs/kbn_security_plugin_types_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-plugin-types-server title: "@kbn/security-plugin-types-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-plugin-types-server plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-plugin-types-server'] --- import kbnSecurityPluginTypesServerObj from './kbn_security_plugin_types_server.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index f6c1f56249e94..cb6385416a3b4 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index 79042522e66ac..674da6f2e3e77 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 15e899799f590..4e0f7253ff473 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 18e9ef6ccac45..ab73f94e454c3 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 326e27dbc1edb..0ba33939c6a51 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index c24741ae5838e..2d9e5411ae151 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index d9fc4da298784..038c478e2bc70 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index b62ff38b32357..af97aa7a25549 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index dad70e27acc43..d1cbde84b3714 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index acb8f05fc9a9c..63c86f5edbda9 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 1da467d78946a..92b8e544e0f4a 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 99ce515f12797..110456ec9f5b6 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 78b159836aef3..b65b81392d714 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 312fccd054c47..68b1b87b02c19 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index ce3125fc94091..beefb773a4dfe 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index f98ad3c88c6f5..38ed263fb2451 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index c358c03853b88..4259544aadc0a 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index fcaf221b29043..b3d37800c1761 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 449bd40dd901d..5612f85b54a56 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 983800f85e786..243a296d06052 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index b4e6f07b13746..f60f80fc0aaab 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index c76a5552a7e6e..a1781cf5c3618 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index c666fdb7efac4..1a53187f2582a 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index 09c664ec88bd7..3578faf0350e0 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 3ef7af4c5abf5..fc8f65f7535c8 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index 5ffc0c80f6e2f..59e36905580f6 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 02df932ebc096..f42c1170cb171 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index 69a6525d74894..7621e02479b90 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index 6ea53d1c9b045..c17759f59ec6c 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index a7d3792b8f486..791d9c3026e88 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index 5fa4a4fcf7086..7cd453bcbe108 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index 6f9cd35248b6f..c44f66ce710a5 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index 94839211fe301..ed0d094f5e986 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 9ff57dc422a9c..a4db1d36c69bb 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 98159f4cf2280..9a266a460f4eb 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index f740bd50809bc..440e4447d4f6f 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index 001480a720e6f..e79489b57ec5e 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 8488046346508..c0d7888c590af 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index 756afd28a2ab0..4b9d18436e320 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 11b17c59d9407..a5bd19ac36b4c 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index e69066fd658e1..d532c361bd6b9 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 37abcd2c84e2d..22d52439d4ba8 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index bdd641263c617..e8aaf66f82af6 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 90a65998ca67f..70ca6a220612f 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 038ff15309377..8719bd0ed31fd 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 6b2f9f66f52b9..802e553896249 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index b41876872e32d..566ff58dbbd61 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index e78f88e0c0390..24c563ba05dc4 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index 58980921c9853..7075642d73426 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index 2bedfa2a2a67d..53b9214599259 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index 84908b9d83e93..cbb7994d58bb5 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index 57add91686671..dd6abd20abf43 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 75e8b6e02cab9..088fc0f969e8a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 0a308044f408d..0f8ac6460262c 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index a76b273d3ee3a..f59f673e6bf08 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 37c0af20730c7..996f1a2f68d61 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 2cdba9ff6e844..66c914b5206f6 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index 0a44a59c2184b..008fd27e011c0 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index cc798934af389..b0417308140e8 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index c6fddc888402e..13b8b9b62b4f1 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index 4123577f698e9..73e8f4567070f 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 19b620356d341..6dcd994357054 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index ee79198737c8d..08ec759951b50 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index 7669491b384b8..bc1d1ebfcf6dd 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index 91f4cc1e346d9..31b8eae527882 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index bf6f18a661ff3..0fefa0685a833 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index ec4b06b70ea5f..5b4f12bfe1e59 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index 9cc367fd800b1..843ad535b5839 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_tabbed_modal.mdx b/api_docs/kbn_shared_ux_tabbed_modal.mdx index 4ffe510c673b5..6bd2784238bc6 100644 --- a/api_docs/kbn_shared_ux_tabbed_modal.mdx +++ b/api_docs/kbn_shared_ux_tabbed_modal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-tabbed-modal title: "@kbn/shared-ux-tabbed-modal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-tabbed-modal plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-tabbed-modal'] --- import kbnSharedUxTabbedModalObj from './kbn_shared_ux_tabbed_modal.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index a93d15bb0b582..fed143b514559 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index dfe594f59dc58..89f1999273e10 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_solution_nav_oblt.mdx b/api_docs/kbn_solution_nav_oblt.mdx index 6f8554f49d68c..7f4ad2c982dc2 100644 --- a/api_docs/kbn_solution_nav_oblt.mdx +++ b/api_docs/kbn_solution_nav_oblt.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-solution-nav-oblt title: "@kbn/solution-nav-oblt" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/solution-nav-oblt plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/solution-nav-oblt'] --- import kbnSolutionNavObltObj from './kbn_solution_nav_oblt.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 68a5655feb3ac..7bf1f512e774a 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_sort_predicates.mdx b/api_docs/kbn_sort_predicates.mdx index d6b473077c262..1a95854bb82bc 100644 --- a/api_docs/kbn_sort_predicates.mdx +++ b/api_docs/kbn_sort_predicates.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-sort-predicates title: "@kbn/sort-predicates" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/sort-predicates plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-predicates'] --- import kbnSortPredicatesObj from './kbn_sort_predicates.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index d4efc8b1a3ff2..330e06018adfc 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 4fbb610874f04..c040994615f20 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index fc2864d5177c8..2424406498c3c 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 36069cc7e5da7..e8e021aa2acd8 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index c174c58d92058..69943b067832b 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_eui_helpers.mdx b/api_docs/kbn_test_eui_helpers.mdx index 228abae8fe492..f02eaff0610a5 100644 --- a/api_docs/kbn_test_eui_helpers.mdx +++ b/api_docs/kbn_test_eui_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-eui-helpers title: "@kbn/test-eui-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-eui-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-eui-helpers'] --- import kbnTestEuiHelpersObj from './kbn_test_eui_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index b25c3c2800b5d..3cab7d94ced45 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 28ea933713110..be8ebc782da41 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index 4eea013748afa..869147f36ac5c 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_timerange.mdx b/api_docs/kbn_timerange.mdx index d7456b738b638..0a7712464510b 100644 --- a/api_docs/kbn_timerange.mdx +++ b/api_docs/kbn_timerange.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-timerange title: "@kbn/timerange" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/timerange plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/timerange'] --- import kbnTimerangeObj from './kbn_timerange.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 9600f3f8ecf6f..b3e36a7e7667d 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_triggers_actions_ui_types.mdx b/api_docs/kbn_triggers_actions_ui_types.mdx index a5c0aedac2b89..9e4f1729a8f68 100644 --- a/api_docs/kbn_triggers_actions_ui_types.mdx +++ b/api_docs/kbn_triggers_actions_ui_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-triggers-actions-ui-types title: "@kbn/triggers-actions-ui-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/triggers-actions-ui-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/triggers-actions-ui-types'] --- import kbnTriggersActionsUiTypesObj from './kbn_triggers_actions_ui_types.devdocs.json'; diff --git a/api_docs/kbn_try_in_console.mdx b/api_docs/kbn_try_in_console.mdx index edf52c8cc4395..5bf01e24d3fae 100644 --- a/api_docs/kbn_try_in_console.mdx +++ b/api_docs/kbn_try_in_console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-try-in-console title: "@kbn/try-in-console" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/try-in-console plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/try-in-console'] --- import kbnTryInConsoleObj from './kbn_try_in_console.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 0bb9b907b443b..5626d3f7007e4 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 69abd42f74ad8..a978ce90694be 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index 3c2a972fb78eb..322ab2745b129 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index cc8be5888164f..0bf30d61e4f12 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 6f4c9d037d610..44e431d7f7eeb 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index a2df8fe1129db..11800508cbbca 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index b736edb28cc59..71dbf7ca86e18 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 437f2fc9b4b32..fa09b841e159c 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_unsaved_changes_badge.mdx b/api_docs/kbn_unsaved_changes_badge.mdx index fd5e6e76735e6..d799dffe9d929 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index eda1dd3807b63..b485c22f6a3bc 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index 5d213f7e38db8..e9583f20e73dc 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index a83a7ba0716bf..dd9ab7fbf100c 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index c217cad220205..de8cc6d545fdd 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index bda43243d4ad5..b9aed7923aa44 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index f8550e8934138..b1a58704a819f 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx index e18522f867474..5b19236b3f251 100644 --- a/api_docs/kbn_visualization_utils.mdx +++ b/api_docs/kbn_visualization_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-utils title: "@kbn/visualization-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] --- import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 8619fa38b538b..cc0897e8f1951 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index 76d5784f088a5..0e3075d88610e 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index b9427f82c7946..7468ae6251191 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 69fd5beb8b033..ce3f361c4abb9 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 9232b879706ff..ab3086e5a2624 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index d1ec3a99606d2..1a9418531586f 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index bb3a7b4c023cc..682e97024751a 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index ef3ff0add2502..9b3d47afd15ec 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 8854d670d581e..ee27c32789e28 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index e09bc8cc4cb27..9eabc764edc4d 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 9ea1c3dcc9648..fe00f7ee3914d 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index 6798f9529633f..49277ae1941e8 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 8c92df255302b..e4e8c3fdc9b7d 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/logs_data_access.mdx b/api_docs/logs_data_access.mdx index 254cb5879f686..6cc96122a5c49 100644 --- a/api_docs/logs_data_access.mdx +++ b/api_docs/logs_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsDataAccess title: "logsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the logsDataAccess plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsDataAccess'] --- import logsDataAccessObj from './logs_data_access.devdocs.json'; diff --git a/api_docs/logs_explorer.mdx b/api_docs/logs_explorer.mdx index ced198a674de2..cf8aee10ed6b1 100644 --- a/api_docs/logs_explorer.mdx +++ b/api_docs/logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsExplorer title: "logsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logsExplorer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsExplorer'] --- import logsExplorerObj from './logs_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 3f3cf17ff3716..d83623dacfacb 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 5f97bdfb6aa88..1ab44cb9f5584 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 104f76c0aaec8..ef335a483b8e3 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 3a627a8747839..3802f228b5c79 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 5210a1fd9593b..4410248509881 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 13cfed8c32f8f..0dc6dd749acf1 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/mock_idp_plugin.mdx b/api_docs/mock_idp_plugin.mdx index 203c7d6952941..7f1448074e2b6 100644 --- a/api_docs/mock_idp_plugin.mdx +++ b/api_docs/mock_idp_plugin.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mockIdpPlugin title: "mockIdpPlugin" image: https://source.unsplash.com/400x175/?github description: API docs for the mockIdpPlugin plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mockIdpPlugin'] --- import mockIdpPluginObj from './mock_idp_plugin.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 5a0c1e2017abf..caad8011283d8 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index cf3d8179ff771..c541ecb49d979 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index bf67f754011df..a7add772f6212 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 4e90a5b253dd2..8dab5293b2941 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 4fd67b0e06c65..fe86e64983896 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 005246404174f..3e925f7cb8831 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 8a7fa5a2caadf..00f314a846fa1 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index ff1ea8744ed83..64063011dc146 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant_app.mdx b/api_docs/observability_a_i_assistant_app.mdx index dc537b2f2d403..ac34d9d193726 100644 --- a/api_docs/observability_a_i_assistant_app.mdx +++ b/api_docs/observability_a_i_assistant_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistantApp title: "observabilityAIAssistantApp" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistantApp plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistantApp'] --- import observabilityAIAssistantAppObj from './observability_a_i_assistant_app.devdocs.json'; diff --git a/api_docs/observability_ai_assistant_management.mdx b/api_docs/observability_ai_assistant_management.mdx index 22d904218d2cb..595a475dbc383 100644 --- a/api_docs/observability_ai_assistant_management.mdx +++ b/api_docs/observability_ai_assistant_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAiAssistantManagement title: "observabilityAiAssistantManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAiAssistantManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAiAssistantManagement'] --- import observabilityAiAssistantManagementObj from './observability_ai_assistant_management.devdocs.json'; diff --git a/api_docs/observability_logs_explorer.mdx b/api_docs/observability_logs_explorer.mdx index ba44f8d10d3ad..8614a4893144f 100644 --- a/api_docs/observability_logs_explorer.mdx +++ b/api_docs/observability_logs_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogsExplorer title: "observabilityLogsExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogsExplorer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogsExplorer'] --- import observabilityLogsExplorerObj from './observability_logs_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index 69bcdbdb4dea7..e1affa96de936 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index d7415511d62ad..f3efee31b3822 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 30104916fc40f..a0132a76a57be 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index 56b998c077d73..d42533f6207c7 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index c128d6c77dee6..192ab067b2123 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -21,7 +21,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 48446 | 241 | 36944 | 1864 | +| 48471 | 241 | 36966 | 1865 | ## Plugin Directory @@ -57,7 +57,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | Add custom data integrations so they can be displayed in the Fleet integrations app | 271 | 0 | 252 | 1 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds the Dashboard app to Kibana | 116 | 0 | 113 | 13 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 54 | 0 | 51 | 0 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3182 | 31 | 2575 | 23 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Data services are useful for searching and querying data from Elasticsearch. Helpful utilities include: a re-usable react query bar, KQL autocomplete, async search, Data Views (Index Patterns) and field formatters. | 3183 | 31 | 2576 | 24 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin provides the ability to create data views via a modal flyout inside Kibana apps | 35 | 0 | 25 | 5 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Reusable data view field editor across Kibana | 72 | 0 | 33 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Data view management app | 2 | 0 | 2 | 0 | @@ -183,7 +183,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 120 | 0 | 59 | 12 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 64 | 0 | 64 | 1 | | | [@elastic/kibana-management](https://github.com/orgs/elastic/teams/kibana-management) | - | 22 | 1 | 22 | 1 | -| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 257 | 0 | 66 | 0 | +| | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 66 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 25 | 0 | 25 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 10 | 0 | 10 | 0 | | synthetics | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | @@ -492,7 +492,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 26 | 0 | 26 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 63 | 1 | 63 | 6 | -| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 52 | 0 | 50 | 0 | +| | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 54 | 0 | 52 | 0 | | | [@elastic/kibana-esql](https://github.com/orgs/elastic/teams/kibana-esql) | - | 194 | 0 | 184 | 10 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 52 | 0 | 52 | 1 | @@ -589,7 +589,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 70 | 0 | 64 | 1 | -| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 202 | 0 | 167 | 5 | +| | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | - | 204 | 0 | 169 | 5 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 168 | 0 | 55 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 13 | 0 | 7 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 22 | 0 | 9 | 0 | @@ -623,7 +623,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 125 | 0 | 122 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 76 | 0 | 76 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3672 | 0 | 3672 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 3689 | 0 | 3689 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 18 | 1 | 17 | 1 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 25 | 0 | 25 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | diff --git a/api_docs/presentation_panel.mdx b/api_docs/presentation_panel.mdx index f2e2691c3a11a..28845de2265ef 100644 --- a/api_docs/presentation_panel.mdx +++ b/api_docs/presentation_panel.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationPanel title: "presentationPanel" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationPanel plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationPanel'] --- import presentationPanelObj from './presentation_panel.devdocs.json'; diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 188aeee26d864..bf6199d075bba 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 87a97a4fff1c8..9a2639b4fa879 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 0262ba7413138..d906a10512be9 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 728df6c4da6de..c8362c67df1da 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 7b11bd86c3c73..ef70c99cabe88 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index d59cd6548a15b..833c33a62bd55 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 4c20d7b9fab56..47703dd4ec2bf 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index cc4cc5ab4e695..09f79edf13d94 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index c112f80f4a56c..5aa4c443dc77e 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index d84ad5b34666e..6a588031ad6a2 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 720c89c9c1a07..79bf2c3308329 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index d151f3195927b..833a0bd8db435 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 92e475aa39f67..f54fbea82a02c 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index 7bee8300eb540..d0bca350e0be0 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 38c6a8d0fcc59..90f766692b2f7 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 231bcb2d2d533..3f38fcc654c77 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/search_connectors.mdx b/api_docs/search_connectors.mdx index f1c683fb0748d..0aa12cd1b2c73 100644 --- a/api_docs/search_connectors.mdx +++ b/api_docs/search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchConnectors title: "searchConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the searchConnectors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchConnectors'] --- import searchConnectorsObj from './search_connectors.devdocs.json'; diff --git a/api_docs/search_notebooks.mdx b/api_docs/search_notebooks.mdx index 75c98b3be8bf2..b5583fdfce473 100644 --- a/api_docs/search_notebooks.mdx +++ b/api_docs/search_notebooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchNotebooks title: "searchNotebooks" image: https://source.unsplash.com/400x175/?github description: API docs for the searchNotebooks plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchNotebooks'] --- import searchNotebooksObj from './search_notebooks.devdocs.json'; diff --git a/api_docs/search_playground.mdx b/api_docs/search_playground.mdx index 05c693117a837..84e9db756dd97 100644 --- a/api_docs/search_playground.mdx +++ b/api_docs/search_playground.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/searchPlayground title: "searchPlayground" image: https://source.unsplash.com/400x175/?github description: API docs for the searchPlayground plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'searchPlayground'] --- import searchPlaygroundObj from './search_playground.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 30453dd9c694d..6f7149d03dfdc 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index bda3227864531..d9ba075dcf8bc 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index 298bd41f73b5c..ec94888b8d92b 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index 08ff77a4cfae3..b61524f0d74ee 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 102ab7c7105c5..2952453c01ba6 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index f026346acd676..1861e9c636116 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index bcf48ef39606a..9699504776f54 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 710bd770f896b..213129577c4cc 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index ba59a583a7618..260d8f7d79ff8 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/slo.mdx b/api_docs/slo.mdx index 3ae4618eca759..02c9f97ea436b 100644 --- a/api_docs/slo.mdx +++ b/api_docs/slo.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/slo title: "slo" image: https://source.unsplash.com/400x175/?github description: API docs for the slo plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'slo'] --- import sloObj from './slo.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 095a36a6ee964..9f31baa803dfa 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.devdocs.json b/api_docs/spaces.devdocs.json index 186dd03eab6af..0d6cb86b49fa4 100644 --- a/api_docs/spaces.devdocs.json +++ b/api_docs/spaces.devdocs.json @@ -1782,6 +1782,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-public.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1809,7 +1825,7 @@ "The space to represent with an avatar." ], "signature": [ - "{ id?: string | undefined; name?: string | undefined; description?: string | undefined; color?: string | undefined; initials?: string | undefined; imageUrl?: string | undefined; disabledFeatures?: string[] | undefined; _reserved?: boolean | undefined; }" + "{ id?: string | undefined; name?: string | undefined; description?: string | undefined; color?: string | undefined; initials?: string | undefined; imageUrl?: string | undefined; disabledFeatures?: string[] | undefined; _reserved?: boolean | undefined; solution?: \"search\" | \"security\" | \"observability\" | \"classic\" | undefined; }" ], "path": "x-pack/plugins/spaces/public/space_avatar/types.ts", "deprecated": false, @@ -3674,6 +3690,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-server.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -4908,6 +4940,22 @@ "path": "x-pack/plugins/spaces/common/types/space/v1.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "spaces", + "id": "def-common.Space.solution", + "type": "CompoundType", + "tags": [], + "label": "solution", + "description": [ + "\nSolution selected for this space." + ], + "signature": [ + "\"search\" | \"security\" | \"observability\" | \"classic\" | undefined" + ], + "path": "x-pack/plugins/spaces/common/types/space/v1.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index 6824f54bea5e4..8be13fa634bb7 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 257 | 0 | 66 | 0 | +| 260 | 0 | 66 | 0 | ## Client diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index b21d67d110f2f..9809b82d48751 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index af57ec3c9d24b..468665987fc1c 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index f276bd6db21d5..376912aa32521 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index b2d0b5fab7ced..29f8ea43773dd 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 6019b2e5d002c..7bf7cb53c235d 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 7695b653d7824..8474a6f1f0439 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 5866d778b46d1..8f633ad94e16e 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index b2d363a49f1da..0b283cbd94989 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index 6fd89ca19a002..b06e062c5c58e 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 601b3658ded6a..5e66234735086 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index e6a7ff5508aae..c009ecdff6215 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 9aaf4e432015a..831c39c801a34 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 971f94d248e12..a860c242f5c8d 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index e9741083768d5..74f3b210a7106 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 609595107d987..51ea5a2b8961b 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 84e6a1819d111..e9dbda8e2d1b2 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index f9cdb5e94c6c6..7a68e714da307 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index bd525a0e518d5..b38c98db0cee3 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index ff645649148c4..4dcaa079c4ea9 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 1a27ce90a9511..33f67b71b933b 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 5ed4c1db33ed2..4bd786927cd1b 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index ac14c74d0b776..d88075906fd3b 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index e035d5c82535f..fd595f5b764bd 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index ad506284484e7..1938631263e38 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index f4a58ea922220..252e062f1e769 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index ac7b57fe7cf35..51b3ce29d070e 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 55df8f57187bf..3cf0916b594d7 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 3713b397cc268..26c974385671b 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index b98e6a52b9387..1e5bcab88790c 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 04b1ca8a4bc46..4ecd068c6e914 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 9a42b9ac4d2b8..99129c1890b7e 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 919231244721f..464c56ebf7bef 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index ae662428e9820..83856809848f7 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2024-05-27 +date: 2024-05-28 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From c60c3632462fa359549f7e8fcc84e3267980b8f7 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Tue, 28 May 2024 09:38:11 +0200 Subject: [PATCH 25/59] lens config builder server side support (#183979) --- packages/kbn-lens-embeddable-utils/README.md | 237 +----------------- .../config_builder/config_builder.ts | 8 +- .../config_builder/types.ts | 4 +- .../config_builder/utils.ts | 11 +- .../kbn-lens-embeddable-utils/package.json | 3 +- 5 files changed, 16 insertions(+), 247 deletions(-) diff --git a/packages/kbn-lens-embeddable-utils/README.md b/packages/kbn-lens-embeddable-utils/README.md index 662ba0c92e7cf..e41de4eaecc7d 100644 --- a/packages/kbn-lens-embeddable-utils/README.md +++ b/packages/kbn-lens-embeddable-utils/README.md @@ -1,241 +1,10 @@ # @kbn/lens-embeddable-utils -## Lens Attributes Builder +## Lens Config Builder - The Lens Attributes Builder is a utility for creating JSON objects used to render charts with Lens. It simplifies the process of configuring and building the necessary attributes for different chart types. - -**Notes**: - -This utililty is primarily used by Infra Observability UI and not meant to be used as an official solution provided by the Lens team. - -- The tool has partial support of Lens charts, currently limited to XY and Metric charts. -- XY Bucket and Breakdown dimensions are limited respectively to Date Histogram and Top values. + The Lens Config Builder is a utility for creating JSON objects used to render charts with Lens. It simplifies the process of configuring and building the necessary attributes for different chart types. ### Usage -#### Creating a Metric Chart - -To create a metric chart, use the `MetricChart` class and provide the required configuration. Here's an example: - -```ts -const metricChart = new MetricChart({ - layers: new MetricLayer({ - data: { - label: 'Disk Read Throughput', - value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - }), - dataView, - formulaAPI -}); -``` - -#### Creating an XY Chart - -To create an XY chart, use the `XYChart` class and provide the required configuration. Here's an example: - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Normalized Load', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - })], - dataView, - formulaAPI -}); -``` - -#### Variations of the XY Chart - -XYChart has different series type variations. Here is an example of how to build a line (default) and area charts - -#### Line chart - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Inbound (RX)', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - - }], - options: { - buckets: {type: 'date_histogram'}, - seriesType: 'line' // default. it doesn't need to be informed. - } - })], - dataView, - formulaAPI -}); -``` - -#### Area chart - -```ts -const xyChart = new XYChart({ - layers: [new XYDataLayer({ - data: [{ - label: 'Inbound (RX)', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - - }], - options: { - buckets: {type: 'date_histogram'}, - seriesType: 'area' - } - })], - dataView, - formulaAPI -}); -``` - -#### Adding Multiple Layers to an XY Chart - -An XY chart can have multiple layers. Here's an example of containing a Reference Line Layer: - -```ts -const xyChart = new XYChart({ - layers: [ - new XYDataLayer({ - data: [{ - label: 'Disk Read Throughput', - value: "average(system.load.1) / max(system.load.cores)", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - }), - new XYReferenceLineLayer({ - data: [{ - - value: "1", - format: { - id: 'percent', - params: { - decimals: 1, - }, - }, - }], - }), - ], - dataView, - formulaAPI -}); -``` - -#### Adding Multiple Data Sources in the Same Layer - -In an XY chart, it's possible to define multiple data sources within the same layer. - -To configure multiple data sources in an XY data layer, simply provide an array of data to the same YXDataLayer class: - -```ts -const xyChart = new XYChart({ - layers: new YXDataLayer({ - data: [{ - label: 'RX', - value: "average(host.network.ingress.bytes) * 8 / (max(metricset.period, kql='host.network.ingress.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - },{ - label: 'TX', - value: "(average(host.network.egresss.bytes) * 8 / (max(metricset.period, kql='host.network.egresss.bytes: *') / 1000)", - format: { - id: 'bits', - params: { - decimals: 1, - }, - }, - }], - options: { - buckets: {type: 'date_histogram'}, - }, - }), - dataView, - formulaAPI -}); -``` - -#### Building Lens Chart Attributes - -The `LensAttributesBuilder` is responsible for creating the full JSON object that combines the attributes returned by the chart classes. Here's an example: - -```ts -const builder = new LensAttributesBuilder({ visualization: xyChart }); -const attributes = builder.build(); -``` - -The `attributes` object contains the final JSON representation of the chart configuration and can be used to render the chart with Lens. - -### Usage with Lens EmbeddableComponent - -To display the charts rendered with the Lens Attributes Builder, it's recommended to use the Lens `EmbeddableComponent`. The `EmbeddableComponent` abstracts some of the chart styling and other details that would be challenging to handle directly with the Lens Attributes Builder. - -```tsx -const builder = new LensAttributesBuilder({ - visualization: new MetricChart({ - layers: new MetricLayer({ - data: { - label: 'Disk Read Throughput', - value: "counter_rate(max(system.diskio.read.count), kql='system.diskio.read.count: *')", - format: { - id: 'bytes', - params: { - decimals: 1, - }, - }, - }, - }), - dataView, - formulaAPI - }), -}); - -const lensAttributes = builder.build(); - - -``` \ No newline at end of file +Check kibana developer docs https://docs.elastic.dev/kibana-dev-docs/lens/config-builder \ No newline at end of file diff --git a/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts b/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts index a718609b3866f..4a14e20b7aea4 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/config_builder.ts @@ -7,8 +7,8 @@ */ import type { FormulaPublicApi, LensEmbeddableInput } from '@kbn/lens-plugin/public'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { v4 as uuidv4 } from 'uuid'; +import { DataViewsService } from '@kbn/data-views-plugin/common'; import { LensAttributes, LensConfig, LensConfigOptions } from './types'; import { buildGauge, @@ -21,6 +21,8 @@ import { buildPartitionChart, } from './charts'; +export type DataViewsCommon = Pick; + export class LensConfigBuilder { private charts = { metric: buildMetric, @@ -36,10 +38,10 @@ export class LensConfigBuilder { table: buildTable, }; private formulaAPI: FormulaPublicApi | undefined; - private dataViewsAPI: DataViewsPublicPluginStart; + private dataViewsAPI: DataViewsCommon; // formulaApi is optional, as it is not necessary to use it when creating charts with ES|QL - constructor(dataViewsAPI: DataViewsPublicPluginStart, formulaAPI?: FormulaPublicApi) { + constructor(dataViewsAPI: DataViewsCommon, formulaAPI?: FormulaPublicApi) { this.formulaAPI = formulaAPI; this.dataViewsAPI = dataViewsAPI; } diff --git a/packages/kbn-lens-embeddable-utils/config_builder/types.ts b/packages/kbn-lens-embeddable-utils/config_builder/types.ts index 83595dbd7581c..19683adb4d423 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/types.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/types.ts @@ -8,8 +8,8 @@ import type { FormulaPublicApi, TypedLensByValueInput } from '@kbn/lens-plugin/public'; import type { Filter, Query } from '@kbn/es-query'; -import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; +import { DataViewsCommon } from './config_builder'; export type LensAttributes = TypedLensByValueInput['attributes']; export const DEFAULT_LAYER_ID = 'layer_0'; @@ -289,7 +289,7 @@ export interface LensXYConfigBase { yBounds?: LensYBoundsConfig; } export interface BuildDependencies { - dataViewsAPI: DataViewsPublicPluginStart; + dataViewsAPI: DataViewsCommon; formulaAPI?: FormulaPublicApi; } diff --git a/packages/kbn-lens-embeddable-utils/config_builder/utils.ts b/packages/kbn-lens-embeddable-utils/config_builder/utils.ts index 2dcb4205f9610..ffd0a40612a7a 100644 --- a/packages/kbn-lens-embeddable-utils/config_builder/utils.ts +++ b/packages/kbn-lens-embeddable-utils/config_builder/utils.ts @@ -8,11 +8,7 @@ import { v4 as uuidv4 } from 'uuid'; import type { SavedObjectReference } from '@kbn/core-saved-objects-common/src/server_types'; -import type { - DataViewSpec, - DataView, - DataViewsPublicPluginStart, -} from '@kbn/data-views-plugin/public'; +import type { DataViewSpec, DataView } from '@kbn/data-views-plugin/public'; import type { FormBasedPersistedState, GenericIndexPatternColumn, @@ -24,6 +20,7 @@ import type { } from '@kbn/lens-plugin/public/datasources/text_based/types'; import type { AggregateQuery } from '@kbn/es-query'; import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils'; +import { DataViewsCommon } from './config_builder'; import { FormulaValueConfig, LensAnnotationLayer, @@ -126,7 +123,7 @@ export function isFormulaDataset(dataset?: LensDataset) { */ export async function getDataView( index: string, - dataViewsAPI: DataViewsPublicPluginStart, + dataViewsAPI: DataViewsCommon, timeField?: string ) { let dataView: DataView; @@ -226,7 +223,7 @@ export const buildDatasourceStates = async ( dataView: DataView ) => PersistedIndexPatternLayer | FormBasedPersistedState['layers'] | undefined, getValueColumns: (config: any, i: number) => TextBasedLayerColumn[], - dataViewsAPI: DataViewsPublicPluginStart + dataViewsAPI: DataViewsCommon ) => { let layers: Partial = {}; diff --git a/packages/kbn-lens-embeddable-utils/package.json b/packages/kbn-lens-embeddable-utils/package.json index c70d63093593f..13664652021bd 100644 --- a/packages/kbn-lens-embeddable-utils/package.json +++ b/packages/kbn-lens-embeddable-utils/package.json @@ -2,5 +2,6 @@ "name": "@kbn/lens-embeddable-utils", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "homepage": "https://docs.elastic.dev/kibana-dev-docs/lens/config-builder" } \ No newline at end of file From c1d992d281425f51489b98af29bbc079faff8fae Mon Sep 17 00:00:00 2001 From: Elena Stoeva <59341489+ElenaStoeva@users.noreply.github.com> Date: Tue, 28 May 2024 10:09:40 +0100 Subject: [PATCH 26/59] [Console Migration] Add styling for status codes in multiple response output (#183858) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes https://github.com/elastic/kibana/issues/179522 ## Summary This PR adds styling for the status codes in the multiple-response output in Console Monaco. https://github.com/elastic/kibana/assets/59341489/1934cda7-d6c1-41a0-9147-3f71885929c6 Screenshot 2024-05-21 at 16 38 55 In Ace editor: Screenshot 2024-05-16 at 18 01 42 Note: In the Ace editor, we use the [`badge.badge` css classes for token names](https://github.com/elastic/kibana/blob/0f1139f302db4a2a673193346978fa9a3e62ccd9/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.ts#L15), but unfortunately Monaco theme rules don't support class names. I tried setting the [background color property](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.ITokenThemeRule.html#background) in the rules for the status code tokens but this doesn't work because of a bug from Monaco's side, see https://github.com/microsoft/monaco-editor/issues/3868 and https://github.com/microsoft/monaco-editor/issues/586). Therefore, in this PR we only set the text colors. --------- Co-authored-by: Yulia Čech <6585477+yuliacech@users.noreply.github.com> --- .../src/console/lexer_rules/console_editor.ts | 6 +++++ .../src/console/lexer_rules/console_output.ts | 26 ++++++++++++++++++- .../src/console/lexer_rules/shared.ts | 2 -- packages/kbn-monaco/src/console/theme.ts | 20 ++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts b/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts index 22878e000fc6c..97984a83b2c9f 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/console_editor.ts @@ -11,6 +11,7 @@ import { consoleSharedLexerRules, matchTokensWithEOL, matchToken, + matchTokens, } from './shared'; import { monaco } from '../../monaco_imports'; @@ -31,6 +32,11 @@ export const lexerRules: monaco.languages.IMonarchLanguage = { // text matchToken('text', '.+?'), ], + comments: [ + // line comment indicated by # + matchTokens(['comment.punctuation', 'comment.line'], /(#)(.*$)/), + ...consoleSharedLexerRules.tokenizer.comments, + ], method_sep: [ // protocol host with slash matchTokensWithEOL( diff --git a/packages/kbn-monaco/src/console/lexer_rules/console_output.ts b/packages/kbn-monaco/src/console/lexer_rules/console_output.ts index 02e14e35aa3a1..8209820930910 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/console_output.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/console_output.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { consoleSharedLanguageConfiguration, consoleSharedLexerRules } from './shared'; +import { + consoleSharedLanguageConfiguration, + consoleSharedLexerRules, + matchTokensWithEOL, +} from './shared'; import { monaco } from '../../monaco_imports'; export const consoleOutputLanguageConfiguration: monaco.languages.LanguageConfiguration = { @@ -15,4 +19,24 @@ export const consoleOutputLanguageConfiguration: monaco.languages.LanguageConfig export const consoleOutputLexerRules: monaco.languages.IMonarchLanguage = { ...consoleSharedLexerRules, + tokenizer: { + ...consoleSharedLexerRules.tokenizer, + comments: [ + // Line comment indicated by # + // Everything after the # character is matched, stopping right before the status code and status text at the end if they are present + matchTokensWithEOL('comment', /# .+?(?=\s+\d{3}(?: \w+)*$)/, 'root', 'status'), + ...consoleSharedLexerRules.tokenizer.comments, + ], + status: [ + // Following HTTP response status codes conventions + // Informational responses (status codes 100 – 199) + matchTokensWithEOL('status.info', /\b1\d{2}(?: \w+)*$/, 'root'), + // Successful responses (status codes 200 – 299) + matchTokensWithEOL('status.success', /\b2\d{2}(?: \w+)*$/, 'root'), + // Redirection messages (status codes 300 – 399) + matchTokensWithEOL('status.redirect', /\b3\d{2}(?: \w+)*$/, 'root'), + // Client and server error responses (status codes 400 – 599) + matchTokensWithEOL('status.error', /\b[4-5]\d{2}(?: \w+)*$/, 'root'), + ], + }, }; diff --git a/packages/kbn-monaco/src/console/lexer_rules/shared.ts b/packages/kbn-monaco/src/console/lexer_rules/shared.ts index f9ceb0f9f85e9..c46ef69af1002 100644 --- a/packages/kbn-monaco/src/console/lexer_rules/shared.ts +++ b/packages/kbn-monaco/src/console/lexer_rules/shared.ts @@ -112,8 +112,6 @@ export const consoleSharedLexerRules: monaco.languages.IMonarchLanguage = { matchToken('paren.lparen', '{', 'json_root'), ], comments: [ - // line comment indicated by # - matchTokens(['comment.punctuation', 'comment.line'], /(#)(.*$)/), // start a block comment indicated by /* matchToken('comment.punctuation', /\/\*/, 'block_comment'), // line comment indicated by // diff --git a/packages/kbn-monaco/src/console/theme.ts b/packages/kbn-monaco/src/console/theme.ts index 66e2bfab812bd..3a90771354a53 100644 --- a/packages/kbn-monaco/src/console/theme.ts +++ b/packages/kbn-monaco/src/console/theme.ts @@ -37,6 +37,26 @@ export const buildConsoleTheme = (): monaco.editor.IStandaloneThemeData => { ['constant.numeric'], makeHighContrastColor(euiThemeVars.euiColorAccentText)(background) ), + ...buildRuleGroup( + ['status.info'], + makeHighContrastColor(euiThemeVars.euiColorWarningText)(background), + true + ), + ...buildRuleGroup( + ['status.success'], + makeHighContrastColor(euiThemeVars.euiColorSuccessText)(background), + true + ), + ...buildRuleGroup( + ['status.redirect'], + makeHighContrastColor(euiThemeVars.euiColorWarningText)(background), + true + ), + ...buildRuleGroup( + ['status.error'], + makeHighContrastColor(euiThemeVars.euiColorDangerText)(background), + true + ), ...buildRuleGroup(['method'], makeHighContrastColor(methodTextColor)(background)), ...buildRuleGroup(['url'], makeHighContrastColor(urlTextColor)(background)), ], From df15fe1b06c9ee21f8b7ca915aca4669845126af Mon Sep 17 00:00:00 2001 From: Umberto Pepato Date: Tue, 28 May 2024 11:15:04 +0200 Subject: [PATCH 27/59] [ResponseOps][Security Solution] Transfer ownership of the alerts grouping package to ResponseOps (#184131) ## Summary As agreed in the recent ResponseOps/Security/Observability meeting, the ResponseOps team will take ownership of the alerts grouping package (formerly `@kbn/securitysolution-grouping`) in order to provide a solution-agnostic API surface for future usages in Observability and Stack rules. This PR implements this transfer, while also renaming the package to `@kbn/grouping` to reflect the change in scope. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 2 +- .i18nrc.json | 2 +- package.json | 2 +- .../.storybook/main.js | 0 packages/kbn-grouping/README.mdx | 3 +++ .../index.tsx | 0 packages/kbn-grouping/jest.config.js | 26 +++++++++++++++++++ packages/kbn-grouping/kibana.jsonc | 5 ++++ .../package.json | 2 +- .../setup_test.ts | 0 .../accordion_panel/group_stats.test.tsx | 0 .../accordion_panel/group_stats.tsx | 0 .../components/accordion_panel/index.test.tsx | 0 .../src/components/accordion_panel/index.tsx | 0 .../src/components/empty_results_panel.tsx | 0 .../group_selector/custom_field_panel.tsx | 0 .../components/group_selector/index.test.tsx | 0 .../src/components/group_selector/index.tsx | 0 .../src/components/grouping.mock.tsx | 0 .../src/components/grouping.stories.tsx | 0 .../src/components/grouping.test.tsx | 0 .../src/components/grouping.tsx | 0 .../src/components/index.tsx | 0 .../src/components/styles.tsx | 0 .../src/components/translations.ts | 0 .../src/components/types.ts | 0 .../src/containers/index.ts | 0 .../src/containers/query/helpers.test.ts | 0 .../src/containers/query/helpers.ts | 0 .../src/containers/query/index.test.ts | 0 .../src/containers/query/index.ts | 0 .../src/containers/query/types.ts | 0 .../src/helpers.ts | 0 .../src/hooks/state/actions.ts | 0 .../src/hooks/state/index.ts | 0 .../src/hooks/state/reducer.test.ts | 0 .../src/hooks/state/reducer.ts | 0 .../src/hooks/state/selectors.ts | 0 .../src/hooks/types.ts | 0 .../src/hooks/use_get_group_selector.test.tsx | 0 .../src/hooks/use_get_group_selector.tsx | 0 .../src/hooks/use_grouping.test.tsx | 0 .../src/hooks/use_grouping.tsx | 0 .../src/index.ts | 0 .../src/mocks.ts | 0 .../src/telemetry/const.ts | 0 .../tsconfig.json | 0 .../kbn-securitysolution-grouping/README.mdx | 3 --- .../jest.config.js | 26 ------------------- .../kibana.jsonc | 5 ---- src/dev/storybook/aliases.ts | 2 +- tsconfig.base.json | 4 +-- .../cloud_security_grouping.tsx | 4 +-- .../use_cloud_security_grouping.ts | 8 ++---- .../utils/first_non_null_value.ts | 2 +- .../latest_findings/constants.ts | 2 +- .../latest_findings_group_renderer.tsx | 2 +- .../latest_findings/use_grouped_findings.tsx | 2 +- .../use_latest_findings_grouping.tsx | 4 +-- .../public/pages/vulnerabilities/constants.ts | 2 +- .../hooks/use_grouped_vulnerabilities.tsx | 2 +- .../use_latest_vulnerabilities_grouping.tsx | 4 +-- .../latest_vulnerabilities_group_renderer.tsx | 2 +- .../cloud_security_posture/tsconfig.json | 2 +- .../public/common/utils/alerts.ts | 2 +- .../alerts_table/alerts_grouping.tsx | 2 +- .../alerts_table/alerts_sub_grouping.tsx | 8 +++--- .../group_panel_renderers.tsx | 2 +- .../grouping_settings/group_stats.tsx | 2 +- .../grouping_settings/query_builder.ts | 4 +-- .../alerts_table/grouping_settings/types.ts | 2 +- .../use_persistent_controls.tsx | 4 +-- .../plugins/security_solution/tsconfig.json | 2 +- yarn.lock | 8 +++--- 74 files changed, 75 insertions(+), 79 deletions(-) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/.storybook/main.js (100%) create mode 100644 packages/kbn-grouping/README.mdx rename packages/{kbn-securitysolution-grouping => kbn-grouping}/index.tsx (100%) create mode 100644 packages/kbn-grouping/jest.config.js create mode 100644 packages/kbn-grouping/kibana.jsonc rename packages/{kbn-securitysolution-grouping => kbn-grouping}/package.json (72%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/setup_test.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/accordion_panel/group_stats.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/accordion_panel/group_stats.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/accordion_panel/index.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/accordion_panel/index.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/empty_results_panel.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/group_selector/custom_field_panel.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/group_selector/index.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/group_selector/index.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/grouping.mock.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/grouping.stories.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/grouping.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/grouping.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/index.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/styles.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/translations.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/components/types.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/index.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/query/helpers.test.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/query/helpers.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/query/index.test.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/query/index.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/containers/query/types.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/helpers.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/state/actions.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/state/index.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/state/reducer.test.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/state/reducer.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/state/selectors.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/types.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/use_get_group_selector.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/use_get_group_selector.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/use_grouping.test.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/hooks/use_grouping.tsx (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/index.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/mocks.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/src/telemetry/const.ts (100%) rename packages/{kbn-securitysolution-grouping => kbn-grouping}/tsconfig.json (100%) delete mode 100644 packages/kbn-securitysolution-grouping/README.mdx delete mode 100644 packages/kbn-securitysolution-grouping/jest.config.js delete mode 100644 packages/kbn-securitysolution-grouping/kibana.jsonc diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e88634c4220d1..6535d9aabd147 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -470,6 +470,7 @@ x-pack/plugins/global_search_providers @elastic/appex-sharedux x-pack/test/plugin_functional/plugins/global_search_test @elastic/kibana-core x-pack/plugins/graph @elastic/kibana-visualizations x-pack/plugins/grokdebugger @elastic/kibana-management +packages/kbn-grouping @elastic/response-ops packages/kbn-guided-onboarding @elastic/appex-sharedux examples/guided_onboarding_example @elastic/appex-sharedux src/plugins/guided_onboarding @elastic/appex-sharedux @@ -735,7 +736,6 @@ x-pack/packages/security-solution/data_table @elastic/security-threat-hunting-in packages/kbn-securitysolution-ecs @elastic/security-threat-hunting-explore packages/kbn-securitysolution-es-utils @elastic/security-detection-engine packages/kbn-securitysolution-exception-list-components @elastic/security-detection-engine -packages/kbn-securitysolution-grouping @elastic/security-threat-hunting-explore packages/kbn-securitysolution-hook-utils @elastic/security-detection-engine packages/kbn-securitysolution-io-ts-alerting-types @elastic/security-detection-engine packages/kbn-securitysolution-io-ts-list-types @elastic/security-detection-engine diff --git a/.i18nrc.json b/.i18nrc.json index c92cfe8b32366..7854a7855351c 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -58,7 +58,7 @@ "filesManagement": "src/plugins/files_management", "flot": "packages/kbn-flot-charts/lib", "generateCsv": "packages/kbn-generate-csv", - "grouping": "packages/kbn-securitysolution-grouping/src", + "grouping": "packages/kbn-grouping/src", "guidedOnboarding": "src/plugins/guided_onboarding", "guidedOnboardingPackage": "packages/kbn-guided-onboarding", "home": "src/plugins/home", diff --git a/package.json b/package.json index f4f03cb30722e..ec56622c5324c 100644 --- a/package.json +++ b/package.json @@ -503,6 +503,7 @@ "@kbn/global-search-test-plugin": "link:x-pack/test/plugin_functional/plugins/global_search_test", "@kbn/graph-plugin": "link:x-pack/plugins/graph", "@kbn/grokdebugger-plugin": "link:x-pack/plugins/grokdebugger", + "@kbn/grouping": "link:packages/kbn-grouping", "@kbn/guided-onboarding": "link:packages/kbn-guided-onboarding", "@kbn/guided-onboarding-example-plugin": "link:examples/guided_onboarding_example", "@kbn/guided-onboarding-plugin": "link:src/plugins/guided_onboarding", @@ -739,7 +740,6 @@ "@kbn/securitysolution-ecs": "link:packages/kbn-securitysolution-ecs", "@kbn/securitysolution-es-utils": "link:packages/kbn-securitysolution-es-utils", "@kbn/securitysolution-exception-list-components": "link:packages/kbn-securitysolution-exception-list-components", - "@kbn/securitysolution-grouping": "link:packages/kbn-securitysolution-grouping", "@kbn/securitysolution-hook-utils": "link:packages/kbn-securitysolution-hook-utils", "@kbn/securitysolution-io-ts-alerting-types": "link:packages/kbn-securitysolution-io-ts-alerting-types", "@kbn/securitysolution-io-ts-list-types": "link:packages/kbn-securitysolution-io-ts-list-types", diff --git a/packages/kbn-securitysolution-grouping/.storybook/main.js b/packages/kbn-grouping/.storybook/main.js similarity index 100% rename from packages/kbn-securitysolution-grouping/.storybook/main.js rename to packages/kbn-grouping/.storybook/main.js diff --git a/packages/kbn-grouping/README.mdx b/packages/kbn-grouping/README.mdx new file mode 100644 index 0000000000000..1a49426a8fb35 --- /dev/null +++ b/packages/kbn-grouping/README.mdx @@ -0,0 +1,3 @@ +# @kbn/grouping + +Grouping component and query. diff --git a/packages/kbn-securitysolution-grouping/index.tsx b/packages/kbn-grouping/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/index.tsx rename to packages/kbn-grouping/index.tsx diff --git a/packages/kbn-grouping/jest.config.js b/packages/kbn-grouping/jest.config.js new file mode 100644 index 0000000000000..d415d4101ad86 --- /dev/null +++ b/packages/kbn-grouping/jest.config.js @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../..', + roots: ['/packages/kbn-grouping'], + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/packages/kbn-grouping/**/*.{ts,tsx}', + '!/packages/kbn-grouping/**/*.test', + '!/packages/kbn-grouping/**/types/*', + '!/packages/kbn-grouping/**/*.type', + '!/packages/kbn-grouping/**/*.styles', + '!/packages/kbn-grouping/**/mocks/*', + '!/packages/kbn-grouping/**/*.config', + '!/packages/kbn-grouping/**/translations', + '!/packages/kbn-grouping/**/types/*', + ], + setupFilesAfterEnv: ['/packages/kbn-grouping/setup_test.ts'], +}; diff --git a/packages/kbn-grouping/kibana.jsonc b/packages/kbn-grouping/kibana.jsonc new file mode 100644 index 0000000000000..ce91c91cefcda --- /dev/null +++ b/packages/kbn-grouping/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/grouping", + "owner": "@elastic/response-ops" +} diff --git a/packages/kbn-securitysolution-grouping/package.json b/packages/kbn-grouping/package.json similarity index 72% rename from packages/kbn-securitysolution-grouping/package.json rename to packages/kbn-grouping/package.json index 8d54527636296..5b454dcf9f268 100644 --- a/packages/kbn-securitysolution-grouping/package.json +++ b/packages/kbn-grouping/package.json @@ -1,5 +1,5 @@ { - "name": "@kbn/securitysolution-grouping", + "name": "@kbn/grouping", "private": true, "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", diff --git a/packages/kbn-securitysolution-grouping/setup_test.ts b/packages/kbn-grouping/setup_test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/setup_test.ts rename to packages/kbn-grouping/setup_test.ts diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.test.tsx b/packages/kbn-grouping/src/components/accordion_panel/group_stats.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.test.tsx rename to packages/kbn-grouping/src/components/accordion_panel/group_stats.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.tsx b/packages/kbn-grouping/src/components/accordion_panel/group_stats.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/group_stats.tsx rename to packages/kbn-grouping/src/components/accordion_panel/group_stats.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.test.tsx b/packages/kbn-grouping/src/components/accordion_panel/index.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.test.tsx rename to packages/kbn-grouping/src/components/accordion_panel/index.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx b/packages/kbn-grouping/src/components/accordion_panel/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/accordion_panel/index.tsx rename to packages/kbn-grouping/src/components/accordion_panel/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/empty_results_panel.tsx b/packages/kbn-grouping/src/components/empty_results_panel.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/empty_results_panel.tsx rename to packages/kbn-grouping/src/components/empty_results_panel.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/custom_field_panel.tsx b/packages/kbn-grouping/src/components/group_selector/custom_field_panel.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/custom_field_panel.tsx rename to packages/kbn-grouping/src/components/group_selector/custom_field_panel.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx b/packages/kbn-grouping/src/components/group_selector/index.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/index.test.tsx rename to packages/kbn-grouping/src/components/group_selector/index.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx b/packages/kbn-grouping/src/components/group_selector/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/group_selector/index.tsx rename to packages/kbn-grouping/src/components/group_selector/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx b/packages/kbn-grouping/src/components/grouping.mock.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.mock.tsx rename to packages/kbn-grouping/src/components/grouping.mock.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx b/packages/kbn-grouping/src/components/grouping.stories.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.stories.tsx rename to packages/kbn-grouping/src/components/grouping.stories.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx b/packages/kbn-grouping/src/components/grouping.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.test.tsx rename to packages/kbn-grouping/src/components/grouping.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/grouping.tsx b/packages/kbn-grouping/src/components/grouping.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/grouping.tsx rename to packages/kbn-grouping/src/components/grouping.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/index.tsx b/packages/kbn-grouping/src/components/index.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/index.tsx rename to packages/kbn-grouping/src/components/index.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/styles.tsx b/packages/kbn-grouping/src/components/styles.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/styles.tsx rename to packages/kbn-grouping/src/components/styles.tsx diff --git a/packages/kbn-securitysolution-grouping/src/components/translations.ts b/packages/kbn-grouping/src/components/translations.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/translations.ts rename to packages/kbn-grouping/src/components/translations.ts diff --git a/packages/kbn-securitysolution-grouping/src/components/types.ts b/packages/kbn-grouping/src/components/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/components/types.ts rename to packages/kbn-grouping/src/components/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/index.ts b/packages/kbn-grouping/src/containers/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/index.ts rename to packages/kbn-grouping/src/containers/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.test.ts b/packages/kbn-grouping/src/containers/query/helpers.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/helpers.test.ts rename to packages/kbn-grouping/src/containers/query/helpers.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts b/packages/kbn-grouping/src/containers/query/helpers.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/helpers.ts rename to packages/kbn-grouping/src/containers/query/helpers.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts b/packages/kbn-grouping/src/containers/query/index.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/index.test.ts rename to packages/kbn-grouping/src/containers/query/index.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/index.ts b/packages/kbn-grouping/src/containers/query/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/index.ts rename to packages/kbn-grouping/src/containers/query/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/containers/query/types.ts b/packages/kbn-grouping/src/containers/query/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/containers/query/types.ts rename to packages/kbn-grouping/src/containers/query/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/helpers.ts b/packages/kbn-grouping/src/helpers.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/helpers.ts rename to packages/kbn-grouping/src/helpers.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/actions.ts b/packages/kbn-grouping/src/hooks/state/actions.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/actions.ts rename to packages/kbn-grouping/src/hooks/state/actions.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/index.ts b/packages/kbn-grouping/src/hooks/state/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/index.ts rename to packages/kbn-grouping/src/hooks/state/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts b/packages/kbn-grouping/src/hooks/state/reducer.test.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/reducer.test.ts rename to packages/kbn-grouping/src/hooks/state/reducer.test.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts b/packages/kbn-grouping/src/hooks/state/reducer.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/reducer.ts rename to packages/kbn-grouping/src/hooks/state/reducer.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/state/selectors.ts b/packages/kbn-grouping/src/hooks/state/selectors.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/state/selectors.ts rename to packages/kbn-grouping/src/hooks/state/selectors.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/types.ts b/packages/kbn-grouping/src/hooks/types.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/types.ts rename to packages/kbn-grouping/src/hooks/types.ts diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx b/packages/kbn-grouping/src/hooks/use_get_group_selector.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.test.tsx rename to packages/kbn-grouping/src/hooks/use_get_group_selector.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx b/packages/kbn-grouping/src/hooks/use_get_group_selector.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_get_group_selector.tsx rename to packages/kbn-grouping/src/hooks/use_get_group_selector.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx b/packages/kbn-grouping/src/hooks/use_grouping.test.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_grouping.test.tsx rename to packages/kbn-grouping/src/hooks/use_grouping.test.tsx diff --git a/packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx b/packages/kbn-grouping/src/hooks/use_grouping.tsx similarity index 100% rename from packages/kbn-securitysolution-grouping/src/hooks/use_grouping.tsx rename to packages/kbn-grouping/src/hooks/use_grouping.tsx diff --git a/packages/kbn-securitysolution-grouping/src/index.ts b/packages/kbn-grouping/src/index.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/index.ts rename to packages/kbn-grouping/src/index.ts diff --git a/packages/kbn-securitysolution-grouping/src/mocks.ts b/packages/kbn-grouping/src/mocks.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/mocks.ts rename to packages/kbn-grouping/src/mocks.ts diff --git a/packages/kbn-securitysolution-grouping/src/telemetry/const.ts b/packages/kbn-grouping/src/telemetry/const.ts similarity index 100% rename from packages/kbn-securitysolution-grouping/src/telemetry/const.ts rename to packages/kbn-grouping/src/telemetry/const.ts diff --git a/packages/kbn-securitysolution-grouping/tsconfig.json b/packages/kbn-grouping/tsconfig.json similarity index 100% rename from packages/kbn-securitysolution-grouping/tsconfig.json rename to packages/kbn-grouping/tsconfig.json diff --git a/packages/kbn-securitysolution-grouping/README.mdx b/packages/kbn-securitysolution-grouping/README.mdx deleted file mode 100644 index b79cac381c298..0000000000000 --- a/packages/kbn-securitysolution-grouping/README.mdx +++ /dev/null @@ -1,3 +0,0 @@ -# @kbn/securitysolution-grouping - -Grouping component and query. Currently only consumed by security solution alerts table. diff --git a/packages/kbn-securitysolution-grouping/jest.config.js b/packages/kbn-securitysolution-grouping/jest.config.js deleted file mode 100644 index 6ad3880e11f1a..0000000000000 --- a/packages/kbn-securitysolution-grouping/jest.config.js +++ /dev/null @@ -1,26 +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 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. - */ - -module.exports = { - preset: '@kbn/test', - rootDir: '../..', - roots: ['/packages/kbn-securitysolution-grouping'], - coverageReporters: ['text', 'html'], - collectCoverageFrom: [ - '/packages/kbn-securitysolution-grouping/**/*.{ts,tsx}', - '!/packages/kbn-securitysolution-grouping/**/*.test', - '!/packages/kbn-securitysolution-grouping/**/types/*', - '!/packages/kbn-securitysolution-grouping/**/*.type', - '!/packages/kbn-securitysolution-grouping/**/*.styles', - '!/packages/kbn-securitysolution-grouping/**/mocks/*', - '!/packages/kbn-securitysolution-grouping/**/*.config', - '!/packages/kbn-securitysolution-grouping/**/translations', - '!/packages/kbn-securitysolution-grouping/**/types/*', - ], - setupFilesAfterEnv: ['/packages/kbn-securitysolution-grouping/setup_test.ts'], -}; diff --git a/packages/kbn-securitysolution-grouping/kibana.jsonc b/packages/kbn-securitysolution-grouping/kibana.jsonc deleted file mode 100644 index 532eb8f883dfc..0000000000000 --- a/packages/kbn-securitysolution-grouping/kibana.jsonc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "type": "shared-common", - "id": "@kbn/securitysolution-grouping", - "owner": "@elastic/security-threat-hunting-explore" -} diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index cc8b46c136aa2..1bec4f797e2fd 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -41,7 +41,7 @@ export const storybookAliases = { expression_shape: 'src/plugins/expression_shape/.storybook', expression_tagcloud: 'src/plugins/chart_expressions/expression_tagcloud/.storybook', fleet: 'x-pack/plugins/fleet/.storybook', - grouping: 'packages/kbn-securitysolution-grouping/.storybook', + grouping: 'packages/kbn-grouping/.storybook', home: 'src/plugins/home/.storybook', infra: 'x-pack/plugins/observability_solution/infra/.storybook', kibana_react: 'src/plugins/kibana_react/.storybook', diff --git a/tsconfig.base.json b/tsconfig.base.json index 23c8774271af9..d5f8c82c63922 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -934,6 +934,8 @@ "@kbn/graph-plugin/*": ["x-pack/plugins/graph/*"], "@kbn/grokdebugger-plugin": ["x-pack/plugins/grokdebugger"], "@kbn/grokdebugger-plugin/*": ["x-pack/plugins/grokdebugger/*"], + "@kbn/grouping": ["packages/kbn-grouping"], + "@kbn/grouping/*": ["packages/kbn-grouping/*"], "@kbn/guided-onboarding": ["packages/kbn-guided-onboarding"], "@kbn/guided-onboarding/*": ["packages/kbn-guided-onboarding/*"], "@kbn/guided-onboarding-example-plugin": ["examples/guided_onboarding_example"], @@ -1464,8 +1466,6 @@ "@kbn/securitysolution-es-utils/*": ["packages/kbn-securitysolution-es-utils/*"], "@kbn/securitysolution-exception-list-components": ["packages/kbn-securitysolution-exception-list-components"], "@kbn/securitysolution-exception-list-components/*": ["packages/kbn-securitysolution-exception-list-components/*"], - "@kbn/securitysolution-grouping": ["packages/kbn-securitysolution-grouping"], - "@kbn/securitysolution-grouping/*": ["packages/kbn-securitysolution-grouping/*"], "@kbn/securitysolution-hook-utils": ["packages/kbn-securitysolution-hook-utils"], "@kbn/securitysolution-hook-utils/*": ["packages/kbn-securitysolution-hook-utils/*"], "@kbn/securitysolution-io-ts-alerting-types": ["packages/kbn-securitysolution-io-ts-alerting-types"], diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx index 9e6e88ee0cbbb..a95ae51f81eac 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/cloud_security_grouping.tsx @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { useGrouping } from '@kbn/securitysolution-grouping'; -import { ParsedGroupingAggregation } from '@kbn/securitysolution-grouping/src'; +import { useGrouping } from '@kbn/grouping'; +import { ParsedGroupingAggregation } from '@kbn/grouping/src'; import { Filter } from '@kbn/es-query'; import React from 'react'; import { css } from '@emotion/react'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts index 1b856fd343d26..ea1aee0ffcef7 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/use_cloud_security_grouping.ts @@ -5,14 +5,10 @@ * 2.0. */ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import { isNoneGroup, useGrouping } from '@kbn/grouping'; import * as uuid from 'uuid'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { - GroupOption, - GroupPanelRenderer, - GroupStatsRenderer, -} from '@kbn/securitysolution-grouping/src'; +import { GroupOption, GroupPanelRenderer, GroupStatsRenderer } from '@kbn/grouping/src'; import { useUrlQuery } from '../../common/hooks/use_url_query'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts index a8c5da0500e8a..2a8d36309f443 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/cloud_security_grouping/utils/first_non_null_value.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { ECSField } from '@kbn/securitysolution-grouping/src'; +import { ECSField } from '@kbn/grouping/src'; /** * Return first non-null value. If the field contains an array, this will return the first value that isn't null. If the field isn't an array it'll be returned unless it's null. diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts index 5ae8f4eb02ac2..9f7ee15604f35 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/constants.ts @@ -6,7 +6,7 @@ */ import { i18n } from '@kbn/i18n'; -import { GroupOption } from '@kbn/securitysolution-grouping'; +import { GroupOption } from '@kbn/grouping'; import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; import { FindingsBaseURLQuery } from '../../../common/types'; import { CloudSecurityDefaultColumn } from '../../../components/cloud_security_data_table'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx index 6dbc78cbf0857..d40037d156d3f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/latest_findings_group_renderer.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src'; +import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/grouping/src'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { FINDINGS_GROUPING_OPTIONS } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx index a4dcfe5a442c2..5784e1e43bcf3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_grouped_findings.tsx @@ -7,7 +7,7 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IKibanaSearchResponse } from '@kbn/search-types'; -import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/securitysolution-grouping/src'; +import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/grouping/src'; import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { CSP_LATEST_FINDINGS_DATA_VIEW } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx index 17b8f0ab0f54d..802ed52be9228 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getGroupingQuery } from '@kbn/securitysolution-grouping'; +import { getGroupingQuery } from '@kbn/grouping'; import { GroupingAggregation, GroupPanelRenderer, @@ -12,7 +12,7 @@ import { isNoneGroup, NamedAggregation, parseGroupingQuery, -} from '@kbn/securitysolution-grouping/src'; +} from '@kbn/grouping/src'; import { useMemo } from 'react'; import { buildEsQuery, Filter } from '@kbn/es-query'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts index 9ec8e0467d467..f85d35968599a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/constants.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { GroupOption } from '@kbn/securitysolution-grouping'; +import { GroupOption } from '@kbn/grouping'; import { FindingsBaseURLQuery } from '../../common/types'; import { CloudSecurityDefaultColumn } from '../../components/cloud_security_data_table'; import { GROUPING_LABELS } from './translations'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx index a39933da49695..79cb2aad7e60e 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_grouped_vulnerabilities.tsx @@ -7,7 +7,7 @@ import { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IKibanaSearchResponse } from '@kbn/search-types'; -import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/securitysolution-grouping/src'; +import { GenericBuckets, GroupingQuery, RootAggregation } from '@kbn/grouping/src'; import { useQuery } from '@tanstack/react-query'; import { lastValueFrom } from 'rxjs'; import { LATEST_VULNERABILITIES_INDEX_PATTERN } from '../../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx index 00b5e80097ec5..fa90d4f6209bd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/hooks/use_latest_vulnerabilities_grouping.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { getGroupingQuery } from '@kbn/securitysolution-grouping'; +import { getGroupingQuery } from '@kbn/grouping'; import { GroupingAggregation, GroupPanelRenderer, @@ -12,7 +12,7 @@ import { isNoneGroup, NamedAggregation, parseGroupingQuery, -} from '@kbn/securitysolution-grouping/src'; +} from '@kbn/grouping/src'; import { useMemo } from 'react'; import { buildEsQuery, Filter } from '@kbn/es-query'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx index b8531491c42c0..489c8ac0990f8 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/latest_vulnerabilities_group_renderer.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/securitysolution-grouping/src'; +import { GroupPanelRenderer, RawBucket, StatRenderer } from '@kbn/grouping/src'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { getCloudProviderNameFromAbbreviation } from '../../../common/utils/helpers'; diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 907ff68fc3897..1febc701d66d6 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -59,7 +59,7 @@ "@kbn/core-http-server-mocks", "@kbn/field-formats-plugin", "@kbn/data-view-field-editor-plugin", - "@kbn/securitysolution-grouping", + "@kbn/grouping", "@kbn/alerting-plugin", "@kbn/code-editor", "@kbn/code-editor-mock", diff --git a/x-pack/plugins/security_solution/public/common/utils/alerts.ts b/x-pack/plugins/security_solution/public/common/utils/alerts.ts index 031fa556b7ab0..780ab831f8dbb 100644 --- a/x-pack/plugins/security_solution/public/common/utils/alerts.ts +++ b/x-pack/plugins/security_solution/public/common/utils/alerts.ts @@ -9,7 +9,7 @@ import { merge } from '@kbn/std'; import { isPlainObject } from 'lodash'; import type { Ecs } from '@kbn/cases-plugin/common'; import { TableId } from '@kbn/securitysolution-data-table'; -import type { GroupOption } from '@kbn/securitysolution-grouping'; +import type { GroupOption } from '@kbn/grouping'; import * as i18n from './translations'; export const buildAlertsQuery = (alertIds: string[]) => { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx index db2562f4a3b8a..4bcbc61d14833 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_grouping.tsx @@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import type { Filter, Query } from '@kbn/es-query'; -import { isNoneGroup, useGrouping } from '@kbn/securitysolution-grouping'; +import { isNoneGroup, useGrouping } from '@kbn/grouping'; import { isEmpty, isEqual } from 'lodash/fp'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx index eaf9277d4bb48..6976338dfbe82 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/alerts_sub_grouping.tsx @@ -9,12 +9,12 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { v4 as uuidv4 } from 'uuid'; import type { Filter, Query } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; -import type { GroupingAggregation } from '@kbn/securitysolution-grouping'; -import { isNoneGroup } from '@kbn/securitysolution-grouping'; +import type { GroupingAggregation } from '@kbn/grouping'; +import { isNoneGroup } from '@kbn/grouping'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; -import type { DynamicGroupingProps } from '@kbn/securitysolution-grouping/src'; +import type { DynamicGroupingProps } from '@kbn/grouping/src'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import { parseGroupingQuery } from '@kbn/securitysolution-grouping/src'; +import { parseGroupingQuery } from '@kbn/grouping/src'; import type { RunTimeMappings } from '../../../common/store/sourcerer/model'; import { combineQueries } from '../../../common/lib/kuery'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx index ac703e496d617..cd858b74399a2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_panel_renderers.tsx @@ -19,7 +19,7 @@ import { import { euiThemeVars } from '@kbn/ui-theme'; import { isArray } from 'lodash/fp'; import React from 'react'; -import type { GroupPanelRenderer } from '@kbn/securitysolution-grouping/src'; +import type { GroupPanelRenderer } from '@kbn/grouping/src'; import type { AlertsGroupingAggregation } from './types'; import { firstNonNullValue } from '../../../../../common/endpoint/models/ecs_safety_helpers'; import type { GenericBuckets } from '../../../../../common/search_strategy'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx index 8c52242be8c41..96cc365d93fab 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/group_stats.tsx @@ -7,7 +7,7 @@ import { EuiIcon } from '@elastic/eui'; import React from 'react'; -import type { RawBucket, StatRenderer } from '@kbn/securitysolution-grouping'; +import type { RawBucket, StatRenderer } from '@kbn/grouping'; import type { AlertsGroupingAggregation } from './types'; import * as i18n from '../translations'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts index eda89f3d537ef..839bea01780be 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/query_builder.ts @@ -6,8 +6,8 @@ */ import type { BoolQuery } from '@kbn/es-query'; -import type { NamedAggregation } from '@kbn/securitysolution-grouping'; -import { isNoneGroup, getGroupingQuery } from '@kbn/securitysolution-grouping'; +import type { NamedAggregation } from '@kbn/grouping'; +import { isNoneGroup, getGroupingQuery } from '@kbn/grouping'; import type { RunTimeMappings } from '../../../../common/store/sourcerer/model'; interface AlertsGroupingQueryParams { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts index d271551f83460..fcdacf94a0688 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/grouping_settings/types.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { GenericBuckets } from '@kbn/securitysolution-grouping/src'; +import type { GenericBuckets } from '@kbn/grouping/src'; // Elasticsearch returns `null` when a sub-aggregation cannot be computed type NumberOrNull = number | null; export interface AlertsGroupingAggregation { diff --git a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx index f662914c870e9..8acb8f8d672db 100644 --- a/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx +++ b/x-pack/plugins/security_solution/public/detections/hooks/trigger_actions_alert_table/use_persistent_controls.tsx @@ -13,8 +13,8 @@ import { dataTableActions, } from '@kbn/securitysolution-data-table'; import type { ViewSelection, TableId } from '@kbn/securitysolution-data-table'; -import { useGetGroupSelectorStateless } from '@kbn/securitysolution-grouping/src/hooks/use_get_group_selector'; -import { getTelemetryEvent } from '@kbn/securitysolution-grouping/src/telemetry/const'; +import { useGetGroupSelectorStateless } from '@kbn/grouping/src/hooks/use_get_group_selector'; +import { getTelemetryEvent } from '@kbn/grouping/src/telemetry/const'; import { groupIdSelector } from '../../../common/store/grouping/selectors'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 0a74be06da7d0..99457d439339d 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -142,7 +142,7 @@ "@kbn/alerts-as-data-utils", "@kbn/cloud-defend-plugin", "@kbn/expandable-flyout", - "@kbn/securitysolution-grouping", + "@kbn/grouping", "@kbn/securitysolution-data-table", "@kbn/core-analytics-server", "@kbn/analytics-client", diff --git a/yarn.lock b/yarn.lock index 5f1865d8d5f8a..ecd4bf656b872 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4903,6 +4903,10 @@ version "0.0.0" uid "" +"@kbn/grouping@link:packages/kbn-grouping": + version "0.0.0" + uid "" + "@kbn/guided-onboarding-example-plugin@link:examples/guided_onboarding_example": version "0.0.0" uid "" @@ -5963,10 +5967,6 @@ version "0.0.0" uid "" -"@kbn/securitysolution-grouping@link:packages/kbn-securitysolution-grouping": - version "0.0.0" - uid "" - "@kbn/securitysolution-hook-utils@link:packages/kbn-securitysolution-hook-utils": version "0.0.0" uid "" From ca3ada63f36da70d817742b9e55e0a442f1aaa8f Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 28 May 2024 11:52:53 +0200 Subject: [PATCH 28/59] [Fleet] Unskip test for agentless integrations (#184308) Fixes https://github.com/elastic/kibana/issues/184191 ## Summary With merge of https://github.com/elastic/kibana/pull/183045 some tests related to agentless started failing in main. I realized that there was a missing condition in a form, so this PR adds that condition and fixes the failing test. Note: the test wasn't flaky, it was failing on each run after merge. For some reason it never failed on the branch, however there was a concurrent merge of [another PR](https://github.com/elastic/kibana/commit/b86039e7859a019af156e65ea08f9bed510f7832) that touched the same UI and that could have something to do with it. ### Checklist - [ ] [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 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../single_page_layout/hooks/form.tsx | 7 ++-- .../single_page_layout/index.test.tsx | 35 +++++++++++++------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 0ce292cd5cba6..54199ef90a06f 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -262,7 +262,11 @@ export function useOnSubmit({ setFormState('INVALID'); return; } - if (agentCount !== 0 && !isAgentlessIntegration(packageInfo) && formState !== 'CONFIRM') { + if ( + agentCount !== 0 && + !(isAgentlessIntegration(packageInfo) || isAgentlessPackagePolicy(packagePolicy)) && + formState !== 'CONFIRM' + ) { setFormState('CONFIRM'); return; } @@ -297,7 +301,6 @@ export function useOnSubmit({ await sendBulkInstallPackages([...new Set(packagesToPreinstall)]); } } - createdPolicy = await createAgentPolicy({ newAgentPolicy, packagePolicy, diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index 75ef6537ce9bd..ab87cbc8492be 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -129,6 +129,10 @@ afterAll(() => { }); describe('When on the package policy create page', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + const createPageUrlPath = pagePathGetters.add_integration_to_policy({ pkgkey: 'nginx-1.3.0' })[1]; let testRenderer: TestRenderer; @@ -738,6 +742,18 @@ describe('When on the package policy create page', () => { describe('With agentless policy available', () => { beforeEach(async () => { + (useStartServices as jest.MockedFunction).mockReturnValue({ + ...useStartServices(), + cloud: { + ...useStartServices().cloud, + isServerlessEnabled: true, + }, + }); + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: true }); + (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( + getMockPackageInfo({ requiresRoot: false, dataStreamRequiresRoot: false }) + ); + (sendGetOneAgentPolicy as jest.MockedFunction).mockResolvedValue({ data: { item: { id: AGENTLESS_POLICY_ID, name: 'Agentless CSPM', namespace: 'default' } }, }); @@ -756,6 +772,7 @@ describe('When on the package policy create page', () => { }); test('should not force create package policy when not in serverless', async () => { + jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: false }); (useStartServices as jest.MockedFunction).mockReturnValue({ ...useStartServices(), cloud: { @@ -784,15 +801,6 @@ describe('When on the package policy create page', () => { }); test('should force create package policy', async () => { - (useStartServices as jest.MockedFunction).mockReturnValue({ - ...useStartServices(), - cloud: { - ...useStartServices().cloud, - isServerlessEnabled: true, - }, - }); - jest.spyOn(ExperimentalFeaturesService, 'get').mockReturnValue({ agentless: true }); - await act(async () => { fireEvent.click(renderResult.getByText('Existing hosts')!); }); @@ -813,8 +821,7 @@ describe('When on the package policy create page', () => { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/184191 - test.skip('should not show confirmation modal', async () => { + test('should not show confirmation modal', async () => { (sendGetAgentStatus as jest.MockedFunction).mockResolvedValueOnce({ data: { results: { total: 1 } }, }); @@ -845,6 +852,12 @@ const mockApiCalls = (http: MockedFleetStartServices['http']) => { if (path === '/api/fleet/outputs') { return Promise.resolve({ data: { items: [] } }); } + if (path === '/api/fleet/fleet_server_hosts') { + return Promise.resolve({ data: { items: [] } }); + } + if (path === '/api/fleet/agent_download_sources') { + return Promise.resolve({ data: { items: [] } }); + } const err = new Error(`API [GET ${path}] is not MOCKED!`); // eslint-disable-next-line no-console console.log(err); From 5d0f09a25b386dca10b194e5b7dc5899fed908f7 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Tue, 28 May 2024 11:46:10 +0100 Subject: [PATCH 29/59] [SecuritySolution] Retrieve onboarding steps (#181442) ## Summary Fixes https://github.com/elastic/kibana/issues/181190 Relevant PRs: https://github.com/elastic/kibana/pull/143598 | https://github.com/elastic/kibana/pull/163739 ### Steps to Verify: 1. Entering [these](https://p.elstc.co/paste/sEmk++Tb#mjwuX7IN8hIN+kOy5gdtfweQNi9sUl+4lVRAewc6hR+) in your kibana.dev.yml 2. Execute this command to set the Guided onboarding steps to alertsCases ``` curl --location --request PUT 'http://localhost:5601/internal/guided_onboarding/state' \ --header 'kbn-xsrf: cypress-creds' \ --header 'x-elastic-internal-origin: security-solution' \ --header 'Content-Type: application/json' \ --header 'Authorization: Basic {YOUR_AUTH_TOKEN}' \. // If you are using Postman just fill in the Auth tab --data '{ "status": "in_progress", "guide": { "isActive": true, "status": "in_progress", "steps": [ { "id": "add_data", "status": "complete" }, { "id": "rules", "status": "complete" }, { "id": "alertsCases", "status": "active" } ], "guideId": "siem" } }' ``` 3. Make sure you have alerts available. 4. To test the old flyout with Guided onboarding tour, please go to Stack Management > Advanced settings > Expandable flyout **OFF** ### It compatible with the new expandable flyout: 1. It shows `expandable flyout tour` when the guided onboarding tour is **not enabled**. 3. The first two steps should be `hidden` when the `left` expandable is opened. 5. Most of the guided onboarding tour steps should be hidden when `Add to new case` flyout or `Add to existing case` modal opened. 6. Once the test case is created, the `insight section` and `correlation tab` should be opened automatically to fetch cases. 7. `expandable flyout tour` should be visible again after the guided onboarding tour is finished. https://github.com/elastic/kibana/assets/6295984/b19bfce9-ec02-4291-b616-e24d3e984a03 ### It compatible with the old flyout: https://github.com/elastic/kibana/assets/6295984/b10b8bdf-e159-4663-b455-1f4541358a11 ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 --------- Co-authored-by: Sergi Massaneda Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ..._cases_add_to_existing_case_modal.test.tsx | 2 +- .../use_cases_add_to_existing_case_modal.tsx | 2 +- .../cases_global_components.test.tsx | 2 +- .../cases_context/cases_global_components.tsx | 2 +- .../public/components/cases_context/index.tsx | 23 +- .../{ => state}/cases_context_reducer.ts | 4 +- .../state/cases_state_context.ts | 21 ++ .../state/use_is_add_to_case_open.test.tsx | 77 ++++++ .../state/use_is_add_to_case_open.ts | 18 ++ .../use_cases_add_to_new_case_flyout.test.tsx | 2 +- .../use_cases_add_to_new_case_flyout.tsx | 2 +- x-pack/plugins/cases/public/mocks.ts | 2 + x-pack/plugins/cases/public/plugin.test.ts | 1 + x-pack/plugins/cases/public/plugin.ts | 2 + x-pack/plugins/cases/public/types.ts | 2 + .../control_columns/row_action/index.test.tsx | 2 + .../control_columns/row_action/index.tsx | 20 +- .../event_details/event_details.test.tsx | 10 + .../event_details/event_details.tsx | 207 +++++++-------- .../insights/related_cases.test.tsx | 113 ++++----- .../__mocks__/tour_step.tsx | 12 + .../guided_onboarding_tour/tour.tsx | 22 +- .../guided_onboarding_tour/tour_config.ts | 17 ++ .../guided_onboarding_tour/tour_step.test.tsx | 109 +++++--- .../guided_onboarding_tour/tour_step.tsx | 235 ++++++++++-------- .../use_hidden_by_flyout.ts | 60 +++++ .../header_actions/actions.test.tsx | 18 ++ .../global_query_string/helpers.test.tsx | 127 ++++++++++ .../utils/global_query_string/helpers.ts | 24 +- .../use_add_exception_flyout.tsx | 20 +- .../take_action_dropdown/index.test.tsx | 1 + .../render_cell_value.test.tsx | 18 ++ .../render_cell_value.tsx | 22 +- .../left/components/related_cases.tsx | 22 +- .../components/correlations_overview.test.tsx | 35 +++ .../components/correlations_overview.tsx | 14 +- .../components/insights_section.test.tsx | 20 ++ .../right/components/insights_section.tsx | 12 +- .../right/components/tour.test.tsx | 23 +- .../right/components/tour.tsx | 16 +- .../document_details/right/header.test.tsx | 77 ++++++ .../flyout/document_details/right/header.tsx | 60 ++++- .../cases_table/cases_table.test.tsx | 2 + .../event_details/flyout/footer.test.tsx | 1 + .../body/events/event_column_view.test.tsx | 2 +- .../components/timeline/body/index.test.tsx | 1 + 46 files changed, 1101 insertions(+), 383 deletions(-) rename x-pack/plugins/cases/public/components/cases_context/{ => state}/cases_context_reducer.ts (93%) create mode 100644 x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts create mode 100644 x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx create mode 100644 x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts create mode 100644 x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx create mode 100644 x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx index 05eeb2b000a3f..1cfbc59eed308 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.test.tsx @@ -19,7 +19,7 @@ import { useCasesToast } from '../../../common/use_cases_toast'; import { alertComment } from '../../../containers/mock'; import { useCreateAttachments } from '../../../containers/use_create_attachments'; import { CasesContext } from '../../cases_context'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry'; import type { AddToExistingCaseModalProps } from './use_cases_add_to_existing_case_modal'; import { useCasesAddToExistingCaseModal } from './use_cases_add_to_existing_case_modal'; diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx index 0fa30647b60ac..e9d37452e6ea7 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/use_cases_add_to_existing_case_modal.tsx @@ -11,7 +11,7 @@ import { CaseStatuses } from '../../../../common/types/domain'; import type { AllCasesSelectorModalProps } from '.'; import { useCasesToast } from '../../../common/use_cases_toast'; import type { CaseUI } from '../../../containers/types'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesContext } from '../../cases_context/use_cases_context'; import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout'; import type { CaseAttachmentsWithoutOwner } from '../../../types'; diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx index 40793010c789f..be21f3dc8f37b 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.test.tsx @@ -10,7 +10,7 @@ import { getAllCasesSelectorModalNoProviderLazy } from '../../client/ui/get_all_ import { getCreateCaseFlyoutLazyNoProvider } from '../../client/ui/get_create_case_flyout'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; -import { getInitialCasesContextState } from './cases_context_reducer'; +import { getInitialCasesContextState } from './state/cases_context_reducer'; import { CasesGlobalComponents } from './cases_global_components'; jest.mock('../../client/ui/get_create_case_flyout'); diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx index 2add2322b1f0b..54dc933113960 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/cases_global_components.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { getAllCasesSelectorModalNoProviderLazy } from '../../client/ui/get_all_cases_selector_modal'; import { getCreateCaseFlyoutLazyNoProvider } from '../../client/ui/get_create_case_flyout'; -import type { CasesContextState } from './cases_context_reducer'; +import type { CasesContextState } from './state/cases_context_reducer'; export const CasesGlobalComponents = React.memo(({ state }: { state: CasesContextState }) => { return ( diff --git a/x-pack/plugins/cases/public/components/cases_context/index.tsx b/x-pack/plugins/cases/public/components/cases_context/index.tsx index a59a76f8adb6a..85c267f5d05d7 100644 --- a/x-pack/plugins/cases/public/components/cases_context/index.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/index.tsx @@ -15,7 +15,6 @@ import { FilesContext } from '@kbn/shared-ux-file-context'; import type { QueryClient } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query'; -import type { CasesContextStoreAction } from './cases_context_reducer'; import type { CasesFeaturesAllRequired, CasesFeatures, @@ -29,7 +28,9 @@ import { CasesGlobalComponents } from './cases_global_components'; import { DEFAULT_FEATURES } from '../../../common/constants'; import { constructFileKindIdByOwner } from '../../../common/files'; import { DEFAULT_BASE_PATH } from '../../common/navigation'; -import { casesContextReducer, getInitialCasesContextState } from './cases_context_reducer'; +import type { CasesContextStoreAction } from './state/cases_context_reducer'; +import { casesContextReducer, getInitialCasesContextState } from './state/cases_context_reducer'; +import { CasesStateContext } from './state/cases_state_context'; import { isRegisteredOwner } from '../../files'; import { casesQueryClient } from './query_client'; @@ -152,14 +153,16 @@ export const CasesProvider: FC< return ( - - {applyFilesContext( - <> - - {children} - - )} - + + + {applyFilesContext( + <> + + {children} + + )} + + ); }; diff --git a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts b/x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts similarity index 93% rename from x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts rename to x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts index 1ab468e246bdd..420ab2d38eba6 100644 --- a/x-pack/plugins/cases/public/components/cases_context/cases_context_reducer.ts +++ b/x-pack/plugins/cases/public/components/cases_context/state/cases_context_reducer.ts @@ -6,8 +6,8 @@ */ import { assertNever } from '@kbn/std'; -import type { AllCasesSelectorModalProps } from '../all_cases/selector_modal'; -import type { CreateCaseFlyoutProps } from '../create/flyout'; +import type { AllCasesSelectorModalProps } from '../../all_cases/selector_modal'; +import type { CreateCaseFlyoutProps } from '../../create/flyout'; export const getInitialCasesContextState = (): CasesContextState => { return { diff --git a/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts b/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts new file mode 100644 index 0000000000000..14a8c01bef902 --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/cases_state_context.ts @@ -0,0 +1,21 @@ +/* + * 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, { useContext } from 'react'; +import type { CasesContextState } from './cases_context_reducer'; + +export const CasesStateContext = React.createContext(undefined); + +export const useCasesStateContext = () => { + const casesStateContext = useContext(CasesStateContext); + if (!casesStateContext) { + throw new Error( + 'useCasesStateContext must be used within a CasesProvider and have a defined value. See https://github.com/elastic/kibana/blob/main/x-pack/plugins/cases/README.md#cases-ui' + ); + } + return casesStateContext; +}; diff --git a/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx new file mode 100644 index 0000000000000..9974d0cb530d9 --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; +import { useCasesAddToExistingCaseModal } from '../../all_cases/selector_modal/use_cases_add_to_existing_case_modal'; +import { createAppMockRenderer } from '../../../common/mock'; +import { useIsAddToCaseOpen } from './use_is_add_to_case_open'; +import { useCasesToast } from '../../../common/use_cases_toast'; +import { useCasesAddToNewCaseFlyout } from '../../create/flyout/use_cases_add_to_new_case_flyout'; + +jest.mock('../../../common/use_cases_toast'); +const useCasesToastMock = useCasesToast as jest.Mock; +useCasesToastMock.mockReturnValue({ + showInfoToast: jest.fn(), +}); + +const { AppWrapper } = createAppMockRenderer(); + +describe('use is add to existing case modal open hook', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should throw if called outside of a cases context', () => { + const { result } = renderHook(useIsAddToCaseOpen); + expect(result.error?.message).toContain( + 'useCasesStateContext must be used within a CasesProvider and have a defined value' + ); + }); + + it('should return false when the add to case modal and flyout are not open', async () => { + const { result } = renderHook(useIsAddToCaseOpen, { wrapper: AppWrapper }); + expect(result.current).toEqual(false); + }); + + it('should return true when the add to existing case modal opens', async () => { + const { result, rerender } = renderHook( + () => { + return { + modal: useCasesAddToExistingCaseModal(), + isOpen: useIsAddToCaseOpen(), + }; + }, + { wrapper: AppWrapper } + ); + + expect(result.current.isOpen).toEqual(false); + act(() => { + result.current.modal.open(); + }); + rerender(); + expect(result.current.isOpen).toEqual(true); + }); + + it('should return true when the add to new case flyout opens', async () => { + const { result, rerender } = renderHook( + () => { + return { + flyout: useCasesAddToNewCaseFlyout(), + isOpen: useIsAddToCaseOpen(), + }; + }, + { wrapper: AppWrapper } + ); + + expect(result.current.isOpen).toEqual(false); + act(() => { + result.current.flyout.open(); + }); + rerender(); + expect(result.current.isOpen).toEqual(true); + }); +}); diff --git a/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts new file mode 100644 index 0000000000000..7e385e3c6b0fe --- /dev/null +++ b/x-pack/plugins/cases/public/components/cases_context/state/use_is_add_to_case_open.ts @@ -0,0 +1,18 @@ +/* + * 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 { useCasesStateContext } from './cases_state_context'; + +export type UseIsAddToCaseOpen = () => boolean; + +/** + * This hook is to check if the "add to case" is open, either the modal or the flyout + */ +export const useIsAddToCaseOpen: UseIsAddToCaseOpen = () => { + const { selectCaseModal, createCaseFlyout } = useCasesStateContext(); + return selectCaseModal.isModalOpen || createCaseFlyout.isFlyoutOpen; +}; diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx index a7aa207162989..168cae0e478fc 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx @@ -10,7 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import type { FC, PropsWithChildren } from 'react'; import React from 'react'; import { CasesContext } from '../../cases_context'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesAddToNewCaseFlyout } from './use_cases_add_to_new_case_flyout'; import { allCasesPermissions } from '../../../common/mock'; import { ExternalReferenceAttachmentTypeRegistry } from '../../../client/attachment_framework/external_reference_registry'; diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx index ea2290bb49633..6ee5b87bde9fc 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.tsx @@ -10,7 +10,7 @@ import { useCallback, useMemo } from 'react'; import type { CaseAttachmentsWithoutOwner } from '../../../types'; import { useCasesToast } from '../../../common/use_cases_toast'; import type { CaseUI } from '../../../containers/types'; -import { CasesContextStoreActionsList } from '../../cases_context/cases_context_reducer'; +import { CasesContextStoreActionsList } from '../../cases_context/state/cases_context_reducer'; import { useCasesContext } from '../../cases_context/use_cases_context'; import type { CreateCaseFlyoutProps } from './create_case_flyout'; diff --git a/x-pack/plugins/cases/public/mocks.ts b/x-pack/plugins/cases/public/mocks.ts index 3511beda3cccc..e267c108a9b39 100644 --- a/x-pack/plugins/cases/public/mocks.ts +++ b/x-pack/plugins/cases/public/mocks.ts @@ -27,6 +27,7 @@ const uiMock: jest.Mocked = { export const openAddToExistingCaseModalMock = jest.fn(); export const openAddToNewCaseFlyoutMock = jest.fn(); +export const isAddToCaseOpenMock = jest.fn(); const hooksMock: jest.Mocked = { useCasesAddToNewCaseFlyout: jest.fn().mockImplementation(() => ({ @@ -35,6 +36,7 @@ const hooksMock: jest.Mocked = { useCasesAddToExistingCaseModal: jest.fn().mockImplementation(() => ({ open: openAddToExistingCaseModalMock, })), + useIsAddToCaseOpen: isAddToCaseOpenMock, }; const helpersMock: jest.Mocked = { diff --git a/x-pack/plugins/cases/public/plugin.test.ts b/x-pack/plugins/cases/public/plugin.test.ts index bfe00078a04d6..54f9cf070df44 100644 --- a/x-pack/plugins/cases/public/plugin.test.ts +++ b/x-pack/plugins/cases/public/plugin.test.ts @@ -142,6 +142,7 @@ describe('Cases Ui Plugin', () => { hooks: { useCasesAddToExistingCaseModal: expect.any(Function), useCasesAddToNewCaseFlyout: expect.any(Function), + useIsAddToCaseOpen: expect.any(Function), }, ui: { getAllCasesSelectorModal: expect.any(Function), diff --git a/x-pack/plugins/cases/public/plugin.ts b/x-pack/plugins/cases/public/plugin.ts index 44393473767e6..12308fcc2f8b6 100644 --- a/x-pack/plugins/cases/public/plugin.ts +++ b/x-pack/plugins/cases/public/plugin.ts @@ -16,6 +16,7 @@ import { APP_ID, APP_PATH } from '../common/constants'; import { APP_TITLE, APP_DESC } from './common/translations'; import { useCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import { useCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; +import { useIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; import { createClientAPI } from './client/api'; import { canUseCases } from './client/helpers/can_use_cases'; import { getRuleIdFromEvent } from './client/helpers/get_rule_id_from_event'; @@ -190,6 +191,7 @@ export class CasesUiPlugin hooks: { useCasesAddToNewCaseFlyout, useCasesAddToExistingCaseModal, + useIsAddToCaseOpen, }, helpers: { canUseCases: canUseCases(core.application.capabilities), diff --git a/x-pack/plugins/cases/public/types.ts b/x-pack/plugins/cases/public/types.ts index bb8790a299d12..c857446ea042c 100644 --- a/x-pack/plugins/cases/public/types.ts +++ b/x-pack/plugins/cases/public/types.ts @@ -32,6 +32,7 @@ import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverle import type { UseCasesAddToExistingCaseModal } from './components/all_cases/selector_modal/use_cases_add_to_existing_case_modal'; import type { UseCasesAddToNewCaseFlyout } from './components/create/flyout/use_cases_add_to_new_case_flyout'; +import type { UseIsAddToCaseOpen } from './components/cases_context/state/use_is_add_to_case_open'; import type { canUseCases } from './client/helpers/can_use_cases'; import type { getRuleIdFromEvent } from './client/helpers/get_rule_id_from_event'; import type { GetCasesContextProps } from './client/ui/get_cases_context'; @@ -154,6 +155,7 @@ export interface CasesPublicStart { hooks: { useCasesAddToNewCaseFlyout: UseCasesAddToNewCaseFlyout; useCasesAddToExistingCaseModal: UseCasesAddToExistingCaseModal; + useIsAddToCaseOpen: UseIsAddToCaseOpen; }; helpers: { /** diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx index 0dd60a0f9c2f8..a6500a2ba5d10 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.test.tsx @@ -32,6 +32,7 @@ jest.mock('../../../utils/route/use_route_spy'); jest.mock('@kbn/expandable-flyout', () => { return { useExpandableFlyoutApi: () => ({ openFlyout: mockOpenFlyout }), + useExpandableFlyoutState: () => ({ left: false }), }; }); @@ -55,6 +56,7 @@ jest.mock('@kbn/kibana-react-plugin/public', () => { ...original, }; }); +jest.mock('../../guided_onboarding_tour/tour_step'); const mockRouteSpy: RouteSpyState = { pageName: SecurityPageName.overview, diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx index bc9969d2a7e42..181b707e88d2d 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx @@ -30,6 +30,8 @@ import type { TimelineItem, TimelineNonEcsData } from '../../../../../common/sea import type { ColumnHeaderOptions, OnRowSelected } from '../../../../../common/types/timeline'; import { TimelineId } from '../../../../../common/types'; import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; +import { useTourContext } from '../../guided_onboarding_tour'; +import { AlertsCasesTourSteps, SecurityStepId } from '../../guided_onboarding_tour/tour_config'; type Props = EuiDataGridCellValueElementProps & { columnHeaders: ColumnHeaderOptions[]; @@ -83,6 +85,11 @@ const RowActionComponent = ({ const isExpandableFlyoutInCreateRuleEnabled = useIsExperimentalFeatureEnabled( 'expandableFlyoutInCreateRuleEnabled' ); + const { activeStep, isTourShown } = useTourContext(); + const shouldFocusOnOverviewTab = + (activeStep === AlertsCasesTourSteps.expandEvent || + activeStep === AlertsCasesTourSteps.reviewAlertDetailsFlyout) && + isTourShown(SecurityStepId.alertsCases); const columnValues = useMemo( () => @@ -123,6 +130,7 @@ const RowActionComponent = ({ openFlyout({ right: { id: DocumentDetailsRightPanelKey, + path: shouldFocusOnOverviewTab ? { tab: 'overview' } : undefined, params: { id: eventId, indexName, @@ -156,7 +164,17 @@ const RowActionComponent = ({ }) ); } - }, [dispatch, eventId, indexName, openFlyout, tabType, tableId, showExpandableFlyout, telemetry]); + }, [ + eventId, + indexName, + showExpandableFlyout, + tableId, + openFlyout, + shouldFocusOnOverviewTab, + telemetry, + dispatch, + tabType, + ]); const Action = controlColumn.rowCellRender; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index f03e204301d87..d7e4a92fe5a2a 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -57,6 +57,12 @@ jest.mock('../../../detection_engine/rule_management/logic/use_rule_with_fallbac }; }); +jest.mock('../guided_onboarding_tour/tour_step', () => ({ + GuidedOnboardingTourStep: jest.fn(({ children }) => ( +
{children}
+ )), +})); + jest.mock('../link_to'); describe('EventDetails', () => { const defaultProps = { @@ -168,6 +174,10 @@ describe('EventDetails', () => { EVENT_DETAILS_CONTEXT_ID ); }); + + test('renders GuidedOnboardingTourStep', () => { + expect(alertsWrapper.find('[data-test-subj="guided-onboarding"]').exists()).toEqual(true); + }); }); describe('threat intel tab', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx index 6c1dd9bce910a..70d0c29eeda4b 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.tsx @@ -231,104 +231,111 @@ const EventDetailsComponent: React.FC = ({ name: i18n.OVERVIEW, 'data-test-subj': 'overviewTab', content: ( - <> - - - - {threatDetails && threatDetails[0] && ( - - <> - -
{threatDetails[0].title}
-
- - {threatDetails[0].description} - - -
- )} - - {renderer != null && detailsEcsData != null && ( -
- -
{i18n.ALERT_REASON}
-
- - - {renderer.renderRow({ - contextId: EVENT_DETAILS_CONTEXT_ID, - data: detailsEcsData, - isDraggable: isDraggable ?? false, - scopeId, - })} - -
- )} - - - - - - {showThreatSummary && ( - + <> + + + + {threatDetails && threatDetails[0] && ( + + <> + +
{threatDetails[0].title}
+
+ + {threatDetails[0].description} + + +
+ )} + + {renderer != null && detailsEcsData != null && ( +
+ +
{i18n.ALERT_REASON}
+
+ + + {renderer.renderRow({ + contextId: EVENT_DETAILS_CONTEXT_ID, + data: detailsEcsData, + isDraggable: isDraggable ?? false, + scopeId, + })} + +
+ )} + + + + - )} - {isEnrichmentsLoading && ( - <> - - - )} + {showThreatSummary && ( + + )} + + {isEnrichmentsLoading && ( + <> + + + )} - {basicAlertData.ruleId && maybeRule?.note && ( - - )} - + {basicAlertData.ruleId && maybeRule?.note && ( + + )} + + ), } : undefined, [ isAlert, + isTourAnchor, browserFields, scopeId, data, @@ -340,7 +347,7 @@ const EventDetailsComponent: React.FC = ({ detailsEcsData, isDraggable, goToTableTab, - maybeRule?.investigation_fields, + maybeRule?.investigation_fields?.field_names, maybeRule?.note, showThreatSummary, hostRisk, @@ -469,23 +476,17 @@ const EventDetailsComponent: React.FC = ({ ); return ( - - <> - - - - + <> + + + ); }; EventDetailsComponent.displayName = 'EventDetailsComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx index 52a6d5eb1eb42..54c30fa38a588 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/insights/related_cases.test.tsx @@ -7,7 +7,7 @@ import { act, render, screen } from '@testing-library/react'; import React from 'react'; - +import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; import { TestProviders } from '../../../mock'; import { useKibana as mockUseKibana } from '../../../lib/kibana/__mocks__'; import { RelatedCases } from './related_cases'; @@ -17,13 +17,17 @@ import { useTourContext } from '../../guided_onboarding_tour'; import { AlertsCasesTourSteps } from '../../guided_onboarding_tour/tour_config'; const mockedUseKibana = mockUseKibana(); -const mockGetRelatedCases = jest.fn(); -const mockCanUseCases = jest.fn(); -jest.mock('../../guided_onboarding_tour'); +const mockCasesContract = casesPluginMock.createStartContract(); +const mockGetRelatedCases = mockCasesContract.api.getRelatedCases as jest.Mock; +mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); +const mockCanUseCases = mockCasesContract.helpers.canUseCases as jest.Mock; +mockCanUseCases.mockReturnValue(readCasesPermissions()); + +const mockUseTourContext = useTourContext as jest.Mock; + jest.mock('../../../lib/kibana', () => { const original = jest.requireActual('../../../lib/kibana'); - return { ...original, useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), @@ -31,68 +35,63 @@ jest.mock('../../../lib/kibana', () => { ...mockedUseKibana, services: { ...mockedUseKibana.services, - cases: { - api: { - getRelatedCases: mockGetRelatedCases, - }, - helpers: { canUseCases: mockCanUseCases }, - }, + cases: mockCasesContract, }, }), }; }); +jest.mock('../../guided_onboarding_tour'); +const defaultUseTourContextValue = { + activeStep: AlertsCasesTourSteps.viewCase, + incrementStep: () => null, + endTourStep: () => null, + isTourShown: () => false, +}; + +jest.mock('../../guided_onboarding_tour/tour_step'); + const eventId = '1c84d9bff4884dabe6aa1bb15f08433463b848d9269e587078dc56669550d27a'; const scrollToMock = jest.fn(); window.HTMLElement.prototype.scrollIntoView = scrollToMock; describe('Related Cases', () => { beforeEach(() => { - mockCanUseCases.mockReturnValue(readCasesPermissions()); - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: AlertsCasesTourSteps.viewCase, - incrementStep: () => null, - endTourStep: () => null, - isTourShown: () => false, - }); + mockUseTourContext.mockReturnValue(defaultUseTourContextValue); jest.clearAllMocks(); }); + describe('When user does not have cases read permissions', () => { beforeEach(() => { mockCanUseCases.mockReturnValue(noCasesPermissions()); }); + test('should not show related cases when user does not have permissions', async () => { await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.queryByText('cases')).toBeNull(); }); }); + describe('When user does have case read permissions', () => { + beforeEach(() => { + mockCanUseCases.mockReturnValue(readCasesPermissions()); + }); + test('Should show the loading message', async () => { + mockGetRelatedCases.mockReturnValueOnce([]); await act(async () => { - mockGetRelatedCases.mockReturnValue([]); - render( - - - - ); - expect(screen.getByText(CASES_LOADING)).toBeInTheDocument(); + render(, { wrapper: TestProviders }); + expect(screen.queryByText(CASES_LOADING)).toBeInTheDocument(); }); + expect(screen.queryByText(CASES_LOADING)).not.toBeInTheDocument(); }); test('Should show 0 related cases when there are none', async () => { + mockGetRelatedCases.mockReturnValueOnce([]); await act(async () => { - mockGetRelatedCases.mockReturnValue([]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(0))).toBeInTheDocument(); @@ -100,28 +99,19 @@ describe('Related Cases', () => { test('Should show 1 related case', async () => { await act(async () => { - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(1))).toBeInTheDocument(); expect(screen.getByTestId('case-details-link')).toHaveTextContent('Test Case'); }); test('Should show 2 related cases', async () => { + mockGetRelatedCases.mockReturnValueOnce([ + { id: '789', title: 'Test Case 1' }, + { id: '456', title: 'Test Case 2' }, + ]); await act(async () => { - mockGetRelatedCases.mockReturnValue([ - { id: '789', title: 'Test Case 1' }, - { id: '456', title: 'Test Case 2' }, - ]); - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(screen.getByText(CASES_COUNT(2))).toBeInTheDocument(); const cases = screen.getAllByTestId('case-details-link'); @@ -131,13 +121,8 @@ describe('Related Cases', () => { }); test('Should not open the related cases accordion when isTourActive=false', async () => { - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(scrollToMock).not.toHaveBeenCalled(); expect( @@ -146,19 +131,13 @@ describe('Related Cases', () => { }); test('Should automatically open the related cases accordion when isTourActive=true', async () => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: AlertsCasesTourSteps.viewCase, - incrementStep: () => null, - endTourStep: () => null, + // this hook is called twice, so we can not use mockReturnValueOnce + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, isTourShown: () => true, }); - mockGetRelatedCases.mockReturnValue([{ id: '789', title: 'Test Case' }]); await act(async () => { - render( - - - - ); + render(, { wrapper: TestProviders }); }); expect(scrollToMock).toHaveBeenCalled(); expect( diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx new file mode 100644 index 0000000000000..fe1ce6c6b1c1d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/__mocks__/tour_step.tsx @@ -0,0 +1,12 @@ +/* + * 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 from 'react'; + +export const GuidedOnboardingTourStep = jest.fn(({ children }) => ( +
{children}
+)); diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx index 2cf0153a1a3fe..a4fca35acf56b 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour.tsx @@ -23,6 +23,8 @@ export interface TourContextValue { incrementStep: (tourId: SecurityStepId) => void; isTourShown: (tourId: SecurityStepId) => boolean; setStep: (tourId: SecurityStepId, step: AlertsCasesTourSteps) => void; + hidden: boolean; + setAllTourStepsHidden: (h: boolean) => void; } const initialState: TourContextValue = { @@ -31,12 +33,19 @@ const initialState: TourContextValue = { incrementStep: () => {}, isTourShown: () => false, setStep: () => {}, + hidden: false, + setAllTourStepsHidden: () => {}, }; const TourContext = createContext(initialState); export const RealTourContextProvider = ({ children }: { children: ReactChild }) => { const { guidedOnboarding } = useKibana().services; + const [hidden, setHidden] = useState(false); + + const setAllTourStepsHidden = useCallback((h: boolean) => { + setHidden(h); + }, []); const isRulesTourActive = useObservable( guidedOnboarding?.guidedOnboardingApi @@ -61,13 +70,16 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) const tourStatus = useMemo( () => ({ - [SecurityStepId.rules]: isRulesTourActive, - [SecurityStepId.alertsCases]: isAlertsCasesTourActive, + [SecurityStepId.rules]: { active: isRulesTourActive, hidden: false }, + [SecurityStepId.alertsCases]: { active: isAlertsCasesTourActive, hidden: false }, }), [isRulesTourActive, isAlertsCasesTourActive] ); - const isTourShown = useCallback((tourId: SecurityStepId) => tourStatus[tourId], [tourStatus]); + const isTourShown = useCallback( + (tourId: SecurityStepId) => tourStatus[tourId].active, + [tourStatus] + ); const [activeStep, _setActiveStep] = useState(1); const incrementStep = useCallback((tourId: SecurityStepId) => { @@ -106,13 +118,15 @@ export const RealTourContextProvider = ({ children }: { children: ReactChild }) const context = useMemo(() => { return { + hidden, + setAllTourStepsHidden, activeStep, endTourStep, incrementStep, isTourShown, setStep, }; - }, [activeStep, endTourStep, incrementStep, isTourShown, setStep]); + }, [activeStep, endTourStep, hidden, incrementStep, isTourShown, setAllTourStepsHidden, setStep]); return {children}; }; diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts index ca52149bfc329..dd04b76d061a8 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_config.ts @@ -59,6 +59,23 @@ const defaultConfig = { export const getTourAnchor = (step: number, tourId: SecurityStepId) => `tourStepAnchor-${tourId}-${step}`; +export const hiddenWhenLeftExpandableFlyoutExpanded: Record = { + [SecurityStepId.alertsCases]: [ + AlertsCasesTourSteps.pointToAlertName, + AlertsCasesTourSteps.expandEvent, + ], +}; + +export const hiddenWhenCaseFlyoutExpanded: Record = { + [SecurityStepId.alertsCases]: [ + AlertsCasesTourSteps.pointToAlertName, + AlertsCasesTourSteps.expandEvent, + AlertsCasesTourSteps.reviewAlertDetailsFlyout, + AlertsCasesTourSteps.addAlertToCase, + AlertsCasesTourSteps.viewCase, + ], +}; + const alertsCasesConfig: StepConfig[] = [ { ...defaultConfig, diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx index cf693392f83c4..0490bf882edd0 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.test.tsx @@ -12,15 +12,38 @@ import { AlertsCasesTourSteps, SecurityStepId } from './tour_config'; import { useTourContext } from './tour'; import { mockGlobalState, TestProviders, createMockStore } from '../../mock'; import { TimelineId } from '../../../../common/types'; +import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; +import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__'; +import { useHiddenByFlyout } from './use_hidden_by_flyout'; + +const mockedUseKibana = mockUseKibana(); +const mockCasesContract = casesPluginMock.createStartContract(); +const mockUseIsAddToCaseOpen = mockCasesContract.hooks.useIsAddToCaseOpen as jest.Mock; +mockUseIsAddToCaseOpen.mockReturnValue(false); +const mockUseTourContext = useTourContext as jest.Mock; + +jest.mock('../../lib/kibana', () => { + const original = jest.requireActual('../../lib/kibana'); + return { + ...original, + useToasts: jest.fn().mockReturnValue({ addWarning: jest.fn() }), + useKibana: () => ({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + cases: mockCasesContract, + }, + }), + }; +}); jest.mock('./tour'); -const mockTourStep = jest - .fn() - .mockImplementation(({ children, footerAction }: EuiTourStepProps) => ( - - {children} {footerAction} - - )); + +const useHiddenByFlyoutMock = useHiddenByFlyout as jest.Mock; +jest.mock('./use_hidden_by_flyout', () => ({ + useHiddenByFlyout: jest.fn(), +})); + jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -29,23 +52,37 @@ jest.mock('@elastic/eui', () => { EuiTourStep: (props: any) => mockTourStep(props), }; }); + +const mockTourStep = jest + .fn() + .mockImplementation(({ children, footerAction }: EuiTourStepProps) => ( + + {children} {footerAction} + + )); + const defaultProps = { isTourAnchor: true, - step: 1, + step: AlertsCasesTourSteps.pointToAlertName, tourId: SecurityStepId.alertsCases, }; const mockChildren =

{'random child element'}

; +const incrementStep = jest.fn(); + +const defaultUseTourContextValue = { + activeStep: AlertsCasesTourSteps.pointToAlertName, + incrementStep, + endTourStep: jest.fn(), + isTourShown: jest.fn(() => true), + hidden: false, +}; describe('GuidedOnboardingTourStep', () => { - const incrementStep = jest.fn(); beforeEach(() => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: 1, - incrementStep, - isTourShown: () => true, - }); jest.clearAllMocks(); + mockUseTourContext.mockReturnValue(defaultUseTourContextValue); + useHiddenByFlyoutMock.mockReturnValue(false); }); it('renders as a tour step', () => { const { getByTestId } = render( @@ -57,6 +94,17 @@ describe('GuidedOnboardingTourStep', () => { expect(tourStep).toBeInTheDocument(); expect(header).toBeInTheDocument(); }); + it('useHiddenByFlyout equals to true, just render children', () => { + useHiddenByFlyoutMock.mockReturnValue(true); + const { getByTestId, queryByTestId } = render( + {mockChildren}, + { wrapper: TestProviders } + ); + const tourStep = queryByTestId('tourStepMock'); + const header = getByTestId('h1'); + expect(tourStep).not.toBeInTheDocument(); + expect(header).toBeInTheDocument(); + }); it('isTourAnchor={false}, just render children', () => { const { getByTestId, queryByTestId } = render( @@ -100,19 +148,14 @@ describe('GuidedOnboardingTourStep', () => { describe('SecurityTourStep', () => { const { isTourAnchor: _, ...stepDefaultProps } = defaultProps; beforeEach(() => { - (useTourContext as jest.Mock).mockReturnValue({ - activeStep: 1, - incrementStep: jest.fn(), - isTourShown: () => true, - }); + (useTourContext as jest.Mock).mockReturnValue(defaultUseTourContextValue); jest.clearAllMocks(); }); it('does not render if tour step does not exist', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 99, - incrementStep: jest.fn(), - isTourShown: () => true, }); render( @@ -134,10 +177,10 @@ describe('SecurityTourStep', () => { }); it('does not render if security tour step is not shown', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 1, - incrementStep: jest.fn(), - isTourShown: () => false, + isTourShown: jest.fn(() => false), }); render({mockChildren}, { wrapper: TestProviders, @@ -166,10 +209,9 @@ describe('SecurityTourStep', () => { }); it('renders next button', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 3, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { getByTestId } = render( @@ -182,9 +224,8 @@ describe('SecurityTourStep', () => { it('if a step has an anchor declared, the tour step should be a sibling of the mockChildren', () => { (useTourContext as jest.Mock).mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 3, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { container } = render( @@ -203,10 +244,9 @@ describe('SecurityTourStep', () => { }); it('if a step does not an anchor declared, the tour step should be the parent of the mockChildren', () => { - (useTourContext as jest.Mock).mockReturnValue({ + mockUseTourContext.mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 2, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { container } = render( @@ -265,9 +305,8 @@ describe('SecurityTourStep', () => { it('does not render next button if step hideNextButton=true ', () => { (useTourContext as jest.Mock).mockReturnValue({ + ...defaultUseTourContextValue, activeStep: 6, - incrementStep: jest.fn(), - isTourShown: () => true, }); const { queryByTestId } = render( diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx index 156604160be74..4f3d06eac53d5 100644 --- a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/tour_step.tsx @@ -18,6 +18,7 @@ import { timelineDefaults } from '../../../timelines/store/defaults'; import { timelineSelectors } from '../../../timelines/store'; import { useTourContext } from './tour'; import { AlertsCasesTourSteps, SecurityStepId, securityTourConfig } from './tour_config'; +import { useHiddenByFlyout } from './use_hidden_by_flyout'; interface SecurityTourStep { children?: React.ReactElement; @@ -37,104 +38,113 @@ const StyledTourStep = styled(EuiTourStep) { - const { activeStep, incrementStep, isTourShown } = useTourContext(); - const tourStep = useMemo( - () => securityTourConfig[tourId].find((config) => config.step === step), - [step, tourId] - ); - - const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); - const showTimeline = useShallowEqualSelector( - (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults).show - ); - - const onClickNext = useCallback( - // onClick should call incrementStep itself - () => (onClick ? onClick() : incrementStep(tourId)), - [incrementStep, onClick, tourId] - ); - - // EUI bug, will remove once bug resolve. will link issue here as soon as i have it - const onKeyDown = useCallback((e) => { - if (e.key === 'Enter') { - e.stopPropagation(); +export const SecurityTourStep = React.memo( + ({ children, onClick, step, tourId }: SecurityTourStep) => { + const { activeStep, incrementStep, isTourShown } = useTourContext(); + const tourStep = useMemo( + () => securityTourConfig[tourId].find((config) => config.step === step), + [step, tourId] + ); + + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const showTimeline = useShallowEqualSelector( + (state) => (getTimeline(state, TimelineId.active) ?? timelineDefaults).show + ); + const onClickNext = useCallback( + // onClick should call incrementStep itself + () => (onClick ? onClick() : incrementStep(tourId)), + [incrementStep, onClick, tourId] + ); + + // EUI bug, will remove once bug resolve. will link issue here as soon as i have it + const onKeyDown = useCallback((e) => { + if (e.key === 'Enter') { + e.stopPropagation(); + } + }, []); + + // steps in Cases app are out of context. + // If we mount this step, we know we need to render it + // we are also managing the context on the siem end in the background + const overrideContext = isStepExternallyMounted(tourId, step); + + if ( + tourStep == null || + ((step !== activeStep || !isTourShown(tourId)) && !overrideContext) || + showTimeline + ) { + return children ? children : null; } - }, []); - - // steps in Cases app are out of context. - // If we mount this step, we know we need to render it - // we are also managing the context on the siem end in the background - const overrideContext = isStepExternallyMounted(tourId, step); - - if ( - tourStep == null || - ((step !== activeStep || !isTourShown(tourId)) && !overrideContext) || - showTimeline - ) { - return children ? children : null; - } - const { anchor, content, imageConfig, dataTestSubj, hideNextButton = false, ...rest } = tourStep; - const footerAction: EuiTourStepProps['footerAction'] = !hideNextButton ? ( - - - - ) : ( - <> - {/* Passing empty element instead of undefined. If undefined "Skip tour" button is shown, we do not want that*/} - - ); - - const commonProps = { - ...rest, - content: ( + const { + anchor, + content, + imageConfig, + dataTestSubj, + hideNextButton = false, + ...rest + } = tourStep; + const footerAction: EuiTourStepProps['footerAction'] = !hideNextButton ? ( + + + + ) : ( <> - -

{content}

-
- {imageConfig && ( - <> - - - - )} + {/* Passing empty element instead of undefined. If undefined "Skip tour" button is shown, we do not want that*/} - ), - footerAction, - // we would not have mounted this component if it was not open - isStepOpen: true, - // guided onboarding does not allow skipping tour through the steps - onFinish: () => null, - stepsTotal: securityTourConfig[tourId].length, - panelProps: { - 'data-test-subj': dataTestSubj, - }, - }; - - // tour step either needs children or an anchor element - // see type EuiTourStepAnchorProps - return anchor != null ? ( - <> - - <>{children} - - ) : children != null ? ( - - {children} - - ) : null; -}; + ); + + const commonProps = { + ...rest, + content: ( + <> + +

{content}

+
+ {imageConfig && ( + <> + + + + )} + + ), + footerAction, + // we would not have mounted this component if it was not open + isStepOpen: true, + // guided onboarding does not allow skipping tour through the steps + onFinish: () => null, + stepsTotal: securityTourConfig[tourId].length, + panelProps: { + 'data-test-subj': dataTestSubj, + }, + }; + + // tour step either needs children or an anchor element + // see type EuiTourStepAnchorProps + return anchor != null ? ( + <> + + <>{children} + + ) : children != null ? ( + + {children} + + ) : null; + } +); +SecurityTourStep.displayName = 'SecurityTourStep'; interface GuidedOnboardingTourStep extends SecurityTourStep { // can be false if the anchor is an iterative element @@ -145,11 +155,30 @@ interface GuidedOnboardingTourStep extends SecurityTourStep { // wraps tour anchor component // and gives the tour step itself a place to mount once it is active // mounts the tour step with a delay to ensure the anchor will render first -export const GuidedOnboardingTourStep = ({ - children, - // can be false if the anchor is an iterative element - // do not use this as an "is tour active" check, the SecurityTourStep checks that anyway - isTourAnchor = true, - ...props -}: GuidedOnboardingTourStep) => - isTourAnchor ? {children} : <>{children}; +export const GuidedOnboardingTourStep = React.memo( + ({ + children, + // can be false if the anchor is an iterative element + // do not use this as an "is tour active" check, the SecurityTourStep checks that anyway + isTourAnchor = true, + ...props + }: GuidedOnboardingTourStep) => { + return isTourAnchor ? ( + {children} + ) : ( + <>{children} + ); + } +); +GuidedOnboardingTourStep.displayName = 'GuidedOnboardingTourStep'; + +const SecurityTourStepAnchor = React.memo(({ children, ...props }: SecurityTourStep) => { + const { hidden: allStepsHidden } = useTourContext(); + const hiddenByFlyout = useHiddenByFlyout({ tourId: props.tourId, step: props.step }); + return !allStepsHidden && !hiddenByFlyout ? ( + {children} + ) : ( + <>{children} + ); +}); +SecurityTourStepAnchor.displayName = 'SecurityTourStepAnchor'; diff --git a/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts new file mode 100644 index 0000000000000..c0c96be21610f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/guided_onboarding_tour/use_hidden_by_flyout.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import type { RisonValue } from '@kbn/rison'; +import type { AlertsCasesTourSteps } from './tour_config'; +import { + hiddenWhenCaseFlyoutExpanded, + hiddenWhenLeftExpandableFlyoutExpanded, + SecurityStepId, +} from './tour_config'; +import { useKibana } from '../../lib/kibana'; +import { URL_PARAM_KEY } from '../../hooks/use_url_state'; +import { getObjectFromQueryString } from '../../utils/global_query_string/helpers'; + +interface UseHiddenByFlyoutProps { + tourId: SecurityStepId; + step: AlertsCasesTourSteps; +} + +/* + ** To check if given Guided tour step should be hidden when the LEFT expandable flyout + ** or any case modal is opened + */ +export const useHiddenByFlyout = ({ tourId, step }: UseHiddenByFlyoutProps) => { + const { useIsAddToCaseOpen } = useKibana().services.cases.hooks; + const isAddToCaseOpen = useIsAddToCaseOpen(); + + const { search } = useLocation(); + + const expandableFlyoutKey = useMemo( + () => + getObjectFromQueryString<{ left: RisonValue; right: RisonValue }>( + URL_PARAM_KEY.flyout, + search + ), + [search] + ); + + const isExpandableFlyoutExpanded = expandableFlyoutKey?.left; + + const hiddenWhenExpandableFlyoutOpened = useMemo( + () => + isExpandableFlyoutExpanded && + hiddenWhenLeftExpandableFlyoutExpanded[SecurityStepId.alertsCases]?.includes(step), + [isExpandableFlyoutExpanded, step] + ); + + const hiddenWhenCasesModalFlyoutExpanded = useMemo( + () => isAddToCaseOpen && hiddenWhenCaseFlyoutExpanded[tourId]?.includes(step), + [isAddToCaseOpen, tourId, step] + ); + + return hiddenWhenExpandableFlyoutOpened || hiddenWhenCasesModalFlyoutExpanded; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx index c2f6f8af965f8..9c79d28ce2069 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/actions.test.tsx @@ -19,7 +19,12 @@ import { SecurityStepId } from '../guided_onboarding_tour/tour_config'; import { Actions } from './actions'; import { initialUserPrivilegesState as mockInitialUserPrivilegesState } from '../user_privileges/user_privileges_context'; import { useUserPrivileges } from '../user_privileges'; +import { useHiddenByFlyout } from '../guided_onboarding_tour/use_hidden_by_flyout'; +const useHiddenByFlyoutMock = useHiddenByFlyout as jest.Mock; +jest.mock('../guided_onboarding_tour/use_hidden_by_flyout', () => ({ + useHiddenByFlyout: jest.fn(), +})); jest.mock('../guided_onboarding_tour'); jest.mock('../user_privileges'); jest.mock('../../../detections/components/user_info', () => ({ @@ -203,6 +208,19 @@ describe('Actions', () => { expect(wrapper.find(SecurityTourStep).exists()).toEqual(true); }); + test('if left expandable flyout is expanded, SecurityTourStep not active', () => { + useHiddenByFlyoutMock.mockReturnValue(true); + + const wrapper = mount( + + + + ); + + expect(wrapper.find(GuidedOnboardingTourStep).exists()).toEqual(true); + expect(wrapper.find(SecurityTourStep).exists()).toEqual(false); + }); + test('on expand event click and SecurityTourStep is active, incrementStep', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx new file mode 100644 index 0000000000000..69f1b5fcbf4e0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.test.tsx @@ -0,0 +1,127 @@ +/* + * 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 { + isDetectionsPages, + getQueryStringFromLocation, + getParamFromQueryString, + getObjectFromQueryString, + useGetInitialUrlParamValue, + encodeQueryString, + useReplaceUrlParams, + createHistoryEntry, +} from './helpers'; +import { renderHook } from '@testing-library/react-hooks'; +import { createMemoryHistory } from 'history'; +import { Router } from 'react-router-dom'; +import React from 'react'; + +const flyoutString = + "(left:(id:document-details-left,params:(id:'04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c',indexName:.internal.alerts-security.alerts-default-000001,scopeId:alerts-page)),right:(id:document-details-right,params:(id:'04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c',indexName:.internal.alerts-security.alerts-default-000001,scopeId:alerts-page)))"; +const testString = `sourcerer=(default:(id:security-solution-default,selectedPatterns:!(.alerts-security.alerts-default)))&timerange=(global:(linkTo:!(),timerange:(from:%272024-05-14T23:00:00.000Z%27,fromStr:now%2Fd,kind:relative,to:%272024-05-15T22:59:59.999Z%27,toStr:now%2Fd)),timeline:(linkTo:!(),timerange:(from:%272024-05-14T09:32:36.347Z%27,kind:absolute,to:%272024-05-15T09:32:36.347Z%27)))&timeline=(activeTab:query,graphEventId:%27%27,isOpen:!f)&pageFilters=!((exclude:!f,existsSelected:!f,fieldName:kibana.alert.workflow_status,hideActionBar:!t,selectedOptions:!(open),title:Status),(exclude:!f,existsSelected:!f,fieldName:kibana.alert.severity,hideActionBar:!t,selectedOptions:!(),title:Severity),(exclude:!f,existsSelected:!f,fieldName:user.name,hideActionBar:!f,selectedOptions:!(),title:User),(exclude:!f,existsSelected:!f,fieldName:host.name,hideActionBar:!f,selectedOptions:!(),title:Host))&flyout=${flyoutString}&timelineFlyout=()`; + +const flyoutObject = { + left: { + id: 'document-details-left', + params: { + id: '04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c', + indexName: '.internal.alerts-security.alerts-default-000001', + scopeId: 'alerts-page', + }, + }, + right: { + id: 'document-details-right', + params: { + id: '04cf6ea970ab702721f17755b718c1296b5f8b5fcf7b74b053eab9bc885ceb9c', + indexName: '.internal.alerts-security.alerts-default-000001', + scopeId: 'alerts-page', + }, + }, +}; + +describe('helpers', () => { + describe('isDetectionsPages', () => { + it('returns true for detections pages', () => { + expect(isDetectionsPages('alerts')).toBe(true); + expect(isDetectionsPages('rules')).toBe(true); + expect(isDetectionsPages('rules-add')).toBe(true); + expect(isDetectionsPages('rules-create')).toBe(true); + expect(isDetectionsPages('exceptions')).toBe(true); + }); + + it('returns false for non-detections pages', () => { + expect(isDetectionsPages('otherPage')).toBe(false); + }); + }); + + describe('getQueryStringFromLocation', () => { + it('returns the query string without the leading "?"', () => { + expect(getQueryStringFromLocation('?param=value')).toBe('param=value'); + }); + }); + + describe('getParamFromQueryString', () => { + it('returns the value of the specified query parameter', () => { + expect(getParamFromQueryString(testString, 'flyout')).toBe(flyoutString); + }); + + it('returns undefined if the query parameter is not found', () => { + expect(getParamFromQueryString(testString, 'param')).toBeUndefined(); + }); + + it('returns the first value if the query parameter is an array', () => { + const queryString = 'param1=value1¶m1=value2'; + expect(getParamFromQueryString(queryString, 'param1')).toBe('value1'); + }); + }); + + describe('getObjectFromQueryString', () => { + it('returns the decoded value of the specified query parameter', () => { + expect(getObjectFromQueryString('flyout', testString)).toEqual(flyoutObject); + }); + + it('returns null if the query parameter is not found', () => { + expect(getObjectFromQueryString('param', testString)).toBeNull(); + }); + }); + + describe('useGetInitialUrlParamValue', () => { + it('returns a function that gets the initial URL parameter value', () => { + window.history.pushState({}, '', `?${testString}`); + const { result } = renderHook(() => useGetInitialUrlParamValue('flyout')); + expect(result.current()).toEqual(flyoutObject); + }); + }); + + describe('encodeQueryString', () => { + it('returns an encoded query string from the given parameters', () => { + const params = { param1: 'value1', param2: 'value2' }; + expect(encodeQueryString(params)).toBe('param1=value1¶m2=value2'); + }); + }); + + describe('useReplaceUrlParams', () => { + it('replaces URL parameters correctly', () => { + const history = createMemoryHistory(); + const Wrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => ( + {children} + ); + const { result } = renderHook(() => useReplaceUrlParams(), { wrapper: Wrapper }); + + window.history.pushState({}, '', '?param1=value1'); + result.current({ param1: 'value2' }); + expect(history.location.search).toBe('?param1=value2'); + }); + }); + + describe('createHistoryEntry', () => { + it('creates a new history entry', () => { + const initialHistoryLength = window.history.length; + createHistoryEntry(); + expect(window.history.length).toBe(initialHistoryLength + 1); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts index 1be01959e2d0a..af3cd8161a778 100644 --- a/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts @@ -33,6 +33,17 @@ export const getParamFromQueryString = ( return Array.isArray(queryParam) ? queryParam[0] : queryParam; }; +export const getObjectFromQueryString = ( + urlParamKey: string, + search?: string +) => { + const rawParamValue = getParamFromQueryString( + getQueryStringFromLocation(search ?? window.location.search), + urlParamKey + ); + return safeDecode(rawParamValue ?? '') as State | null; +}; + /** * * Gets the value of the URL param from the query string. @@ -44,15 +55,10 @@ export const useGetInitialUrlParamValue = ( ): (() => State | null) => { // window.location.search provides the most updated representation of the url search. // It also guarantees that we don't overwrite URL param managed outside react-router. - const getInitialUrlParamValue = useCallback((): State | null => { - const rawParamValue = getParamFromQueryString( - getQueryStringFromLocation(window.location.search), - urlParamKey - ); - const paramValue = safeDecode(rawParamValue ?? '') as State | null; - - return paramValue; - }, [urlParamKey]); + const getInitialUrlParamValue = useCallback( + (): State | null => getObjectFromQueryString(urlParamKey), + [urlParamKey] + ); return getInitialUrlParamValue; }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx index 85892f4ba5b53..40c855341a1e9 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_exception_flyout.tsx @@ -9,6 +9,7 @@ import { useCallback, useState } from 'react'; import type { ExceptionListTypeEnum } from '@kbn/securitysolution-io-ts-list-types'; import type { inputsModel } from '../../../../common/store'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; interface UseExceptionFlyoutProps { refetch?: inputsModel.Refetch; @@ -32,20 +33,26 @@ export const useExceptionFlyout = ({ onRuleChange, isActiveTimelines, }: UseExceptionFlyoutProps): UseExceptionFlyout => { + const { setAllTourStepsHidden } = useTourContext(); const [openAddExceptionFlyout, setOpenAddExceptionFlyout] = useState(false); const [exceptionFlyoutType, setExceptionFlyoutType] = useState( null ); - const onAddExceptionTypeClick = useCallback((exceptionListType?: ExceptionListTypeEnum): void => { - setExceptionFlyoutType(exceptionListType ?? null); - setOpenAddExceptionFlyout(true); - }, []); + const onAddExceptionTypeClick = useCallback( + (exceptionListType?: ExceptionListTypeEnum): void => { + setExceptionFlyoutType(exceptionListType ?? null); + setAllTourStepsHidden(true); + setOpenAddExceptionFlyout(true); + }, + [setAllTourStepsHidden] + ); const onAddExceptionCancel = useCallback(() => { setExceptionFlyoutType(null); + setAllTourStepsHidden(false); setOpenAddExceptionFlyout(false); - }, []); + }, [setAllTourStepsHidden]); const onAddExceptionConfirm = useCallback( (didRuleChange: boolean, didCloseAlert: boolean, didBulkCloseAlert) => { @@ -55,9 +62,10 @@ export const useExceptionFlyout = ({ if (onRuleChange != null && didRuleChange) { onRuleChange(); } + setAllTourStepsHidden(false); setOpenAddExceptionFlyout(false); }, - [onRuleChange, refetch, isActiveTimelines] + [refetch, isActiveTimelines, onRuleChange, setAllTourStepsHidden] ); return { diff --git a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx index ff1ef27195e69..ce5126664fb14 100644 --- a/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/take_action_dropdown/index.test.tsx @@ -49,6 +49,7 @@ jest.mock('../user_info', () => ({ })); jest.mock('../../../common/lib/kibana'); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step'); jest.mock('../../containers/detection_engine/alerts/use_alerts_privileges', () => ({ useAlertsPrivileges: jest.fn().mockReturnValue({ hasIndexWrite: true, hasKibanaCRUD: true }), diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx index 20afb552e045a..5abe8a3dfbd93 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.test.tsx @@ -29,6 +29,7 @@ jest.mock('../../../common/containers/sourcerer', () => ({ indicesExist: true, }), })); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step'); describe('RenderCellValue', () => { const columnId = '@timestamp'; @@ -82,4 +83,21 @@ describe('RenderCellValue', () => { expect(wrapper.find(DefaultCellRenderer).props()).toEqual(props); }); + + test('it renders a GuidedOnboardingTourStep', () => { + const RenderCellValue = getRenderCellValueHook({ + scopeId: SourcererScopeName.default, + tableId: TableId.test, + }); + + const wrapper = mount( + + + + + + ); + + expect(wrapper.find('[data-test-subj="GuidedOnboardingTourStep"]').exists()).toEqual(true); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx index 413e9beba6015..3262e0bee184e 100644 --- a/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx +++ b/x-pack/plugins/security_solution/public/detections/configurations/security_solution_detections/render_cell_value.tsx @@ -151,27 +151,27 @@ export const RenderCellValue: React.FC ); }, [ - isTourAnchor, - finalData, - browserFieldsByName, header, columnId, + browserFieldsByName, + columnHeaders, ecsData, - linkValues, - rowRenderers, + isTourAnchor, + browserFields, + finalData, + eventId, isDetails, - isExpandable, isDraggable, + isExpandable, isExpanded, + linkValues, + rowIndex, colIndex, - eventId, + rowRenderers, setCellProps, + tableId, truncate, context, - tableId, - browserFields, - rowIndex, - columnHeaders, ]); return columnId === SIGNAL_RULE_NAME_FIELD_NAME && actualSuppressionCount ? ( diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx index bef7b15a7a971..13df33a2deb1b 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/related_cases.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { EuiInMemoryTable } from '@elastic/eui'; import type { RelatedCase } from '@kbn/cases-plugin/common'; @@ -21,7 +21,7 @@ import { ExpandablePanel } from '../../../shared/components/expandable_panel'; const ICON = 'warning'; -const columns: Array> = [ +const getColumns: (data: RelatedCase[]) => Array> = (data) => [ { field: 'title', name: ( @@ -30,13 +30,16 @@ const columns: Array> = [ defaultMessage="Name" /> ), - render: (value: string, caseData: RelatedCase) => ( - - - {caseData.title} - - - ), + render: (value: string, caseData: RelatedCase) => { + const index = data.findIndex((d) => d.id === caseData.id); + return ( + + + {caseData.title} + + + ); + }, }, { field: 'status', @@ -63,6 +66,7 @@ export interface RelatedCasesProps { */ export const RelatedCases: React.FC = ({ eventId }) => { const { loading, error, data, dataCount } = useFetchRelatedCases({ eventId }); + const columns = useMemo(() => getColumns(data), [data]); if (error) { return null; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx index bb4fcdc79bb83..2ae680fc54ba9 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.test.tsx @@ -40,6 +40,8 @@ import { EXPANDABLE_PANEL_HEADER_TITLE_TEXT_TEST_ID, EXPANDABLE_PANEL_TOGGLE_ICON_TEST_ID, } from '../../../shared/components/test_ids'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { AlertsCasesTourSteps } from '../../../../common/components/guided_onboarding_tour/tour_config'; jest.mock('../../shared/hooks/use_show_related_alerts_by_ancestry'); jest.mock('../../shared/hooks/use_show_related_alerts_by_same_source_event'); @@ -100,11 +102,24 @@ jest.mock('../../../../timelines/containers/use_timeline_data_filters', () => ({ })); const mockUseTimelineDataFilters = useTimelineDataFilters as jest.Mock; +jest.mock('../../../../common/components/guided_onboarding_tour', () => ({ + useTourContext: jest.fn(), +})); + const originalEventId = 'originalEventId'; describe('', () => { beforeAll(() => { jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue); + jest.mocked(useTourContext).mockReturnValue({ + hidden: false, + setAllTourStepsHidden: jest.fn(), + activeStep: AlertsCasesTourSteps.viewCase, + endTourStep: jest.fn(), + incrementStep: jest.fn(), + isTourShown: jest.fn(), + setStep: jest.fn(), + }); mockUseTimelineDataFilters.mockReturnValue({ selectedPatterns: ['index'] }); }); @@ -211,4 +226,24 @@ describe('', () => { }, }); }); + + it('should navigate to the left section Insights tab automatically when active step is "view case"', () => { + render( + + + + + + ); + + expect(flyoutContextValue.openLeftPanel).toHaveBeenCalledWith({ + id: DocumentDetailsLeftPanelKey, + path: { tab: LeftPanelInsightsTab, subTab: CORRELATIONS_TAB_ID }, + params: { + id: panelContextValue.eventId, + indexName: panelContextValue.indexName, + scopeId: panelContextValue.scopeId, + }, + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx index e6bf84f039c52..2125eb47316ae 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/correlations_overview.tsx @@ -6,7 +6,7 @@ */ import { get } from 'lodash'; -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { EuiFlexGroup } from '@elastic/eui'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -30,6 +30,11 @@ import { LeftPanelInsightsTab } from '../../left'; import { CORRELATIONS_TAB_ID } from '../../left/components/correlations_details'; import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters'; import { isActiveTimeline } from '../../../../helpers'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { + AlertsCasesTourSteps, + SecurityStepId, +} from '../../../../common/components/guided_onboarding_tour/tour_config'; /** * Correlations section under Insights section, overview tab. @@ -40,6 +45,7 @@ export const CorrelationsOverview: React.FC = () => { const { dataAsNestedObject, eventId, indexName, getFieldsData, scopeId, isPreview } = useRightPanelContext(); const { openLeftPanel } = useExpandableFlyoutApi(); + const { isTourShown, activeStep } = useTourContext(); const { selectedPatterns } = useTimelineDataFilters(isActiveTimeline(scopeId)); @@ -58,6 +64,12 @@ export const CorrelationsOverview: React.FC = () => { }); }, [eventId, openLeftPanel, indexName, scopeId]); + useEffect(() => { + if (isTourShown(SecurityStepId.alertsCases) && activeStep === AlertsCasesTourSteps.viewCase) { + goToCorrelationsTab(); + } + }, [activeStep, goToCorrelationsTab, isTourShown]); + const { show: showAlertsByAncestry, documentId } = useShowRelatedAlertsByAncestry({ getFieldsData, dataAsNestedObject, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx index 81f9c6d54457e..ebba19bd2d648 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.test.tsx @@ -29,6 +29,7 @@ import { useAlertPrevalence } from '../../../../common/containers/alerts/use_ale import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score'; import { useExpandSection } from '../hooks/use_expand_section'; import { useTimelineDataFilters } from '../../../../timelines/containers/use_timeline_data_filters'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; jest.mock('../../../../common/containers/alerts/use_alert_prevalence'); @@ -96,6 +97,11 @@ jest.mock('../hooks/use_fetch_threat_intelligence'); jest.mock('../../shared/hooks/use_prevalence'); +const mockUseTourContext = useTourContext as jest.Mock; +jest.mock('../../../../common/components/guided_onboarding_tour', () => ({ + useTourContext: jest.fn().mockReturnValue({ activeStep: 1, isTourShown: jest.fn(() => true) }), +})); + const renderInsightsSection = (contextValue: RightPanelContext) => render( @@ -163,6 +169,20 @@ describe('', () => { expect(wrapper.getByTestId(INSIGHTS_CONTENT_TEST_ID)).toBeVisible(); }); + it('should render the component expanded if guided onboarding tour is shown', () => { + (useExpandSection as jest.Mock).mockReturnValue(false); + mockUseTourContext.mockReturnValue({ activeStep: 7, isTourShown: jest.fn(() => true) }); + + const contextValue = { + eventId: 'some_Id', + dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser, + getFieldsData: mockGetFieldsData, + } as unknown as RightPanelContext; + + const wrapper = renderInsightsSection(contextValue); + expect(wrapper.getByTestId(INSIGHTS_CONTENT_TEST_ID)).toBeVisible(); + }); + it('should render all children when event kind is signal', () => { (useExpandSection as jest.Mock).mockReturnValue(true); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx index 831124c5539e2..7ea4b67734ae4 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/insights_section.tsx @@ -18,6 +18,11 @@ import { ExpandableSection } from './expandable_section'; import { useRightPanelContext } from '../context'; import { getField } from '../../shared/utils'; import { EventKind } from '../../shared/constants/event_kinds'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour'; +import { + AlertsCasesTourSteps, + SecurityStepId, +} from '../../../../common/components/guided_onboarding_tour/tour_config'; const KEY = 'insights'; @@ -28,7 +33,12 @@ export const InsightsSection = memo(() => { const { getFieldsData } = useRightPanelContext(); const eventKind = getField(getFieldsData('event.kind')); - const expanded = useExpandSection({ title: KEY, defaultValue: false }); + const { activeStep, isTourShown } = useTourContext(); + const isGuidedOnboardingTourShown = + isTourShown(SecurityStepId.alertsCases) && activeStep === AlertsCasesTourSteps.viewCase; + + const expanded = + useExpandSection({ title: KEY, defaultValue: false }) || isGuidedOnboardingTourShown; return ( render( @@ -50,10 +56,11 @@ describe('', () => { services: { ...mockedUseKibana.services, storage: storageMock, + cases: mockCasesContract, }, }); (useIsTimelineFlyoutOpen as jest.Mock).mockReturnValue(false); - + (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => false) }); storageMock.clear(); }); @@ -82,7 +89,19 @@ describe('', () => { expect(queryByText('Next')).not.toBeInTheDocument(); }); - it('should not render tour for non-alerts', () => { + it('should not render tour when guided onboarding tour is active', () => { + (useTourContext as jest.Mock).mockReturnValue({ isTourShown: jest.fn(() => true) }); + const { queryByText, queryByTestId } = renderRightPanelTour({ + ...mockContextValue, + getFieldsData: () => '', + }); + + expect(queryByTestId(`${FLYOUT_TOUR_TEST_ID}-1`)).not.toBeInTheDocument(); + expect(queryByText('Next')).not.toBeInTheDocument(); + }); + + it('should not render tour when case modal is open', () => { + mockUseIsAddToCaseOpen.mockReturnValue(true); const { queryByText, queryByTestId } = renderRightPanelTour({ ...mockContextValue, getFieldsData: () => '', diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx index 5003139a92577..9fd7219007dd8 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/tour.tsx @@ -20,18 +20,32 @@ import { } from '../../shared/constants/panel_keys'; import { EventKind } from '../../shared/constants/event_kinds'; import { useIsTimelineFlyoutOpen } from '../../shared/hooks/use_is_timeline_flyout_open'; +import { useTourContext } from '../../../../common/components/guided_onboarding_tour/tour'; +import { SecurityStepId } from '../../../../common/components/guided_onboarding_tour/tour_config'; +import { useKibana } from '../../../../common/lib/kibana'; /** * Guided tour for the right panel in details flyout */ export const RightPanelTour = memo(() => { + const { useIsAddToCaseOpen } = useKibana().services.cases.hooks; + + const casesFlyoutExpanded = useIsAddToCaseOpen(); + + const { isTourShown: isGuidedOnboardingTourShown } = useTourContext(); + const { openLeftPanel, openRightPanel } = useExpandableFlyoutApi(); const { eventId, indexName, scopeId, isPreview, getFieldsData } = useRightPanelContext(); const eventKind = getField(getFieldsData('event.kind')); const isAlert = eventKind === EventKind.signal; const isTimelineFlyoutOpen = useIsTimelineFlyoutOpen(); - const showTour = isAlert && !isPreview && !isTimelineFlyoutOpen; + const showTour = + isAlert && + !isPreview && + !isTimelineFlyoutOpen && + !isGuidedOnboardingTourShown(SecurityStepId.alertsCases) && + !casesFlyoutExpanded; const goToLeftPanel = useCallback(() => { openLeftPanel({ diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx new file mode 100644 index 0000000000000..81f8e14b8b981 --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; + +import { render } from '@testing-library/react'; +import { PanelHeader } from './header'; +import { allThreeTabs } from './hooks/use_tabs'; +import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step'; +import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; + +jest.mock('./context', () => ({ + useRightPanelContext: jest.fn().mockReturnValue({ dataFormattedForFieldBrowser: [] }), +})); +jest.mock('../../../timelines/components/side_panel/event_details/helpers', () => ({ + useBasicDataFromDetailsData: jest.fn(), +})); +jest.mock('../../../common/components/guided_onboarding_tour/tour_step', () => ({ + GuidedOnboardingTourStep: jest.fn().mockReturnValue(
), +})); + +jest.mock('./components/alert_header_title', () => ({ + AlertHeaderTitle: jest.fn().mockReturnValue(
), +})); + +jest.mock('./components/event_header_title', () => ({ + EventHeaderTitle: jest.fn().mockReturnValue(
), +})); + +const mockUseBasicDataFromDetailsData = useBasicDataFromDetailsData as jest.Mock; +const mockGuidedOnboardingTourStep = GuidedOnboardingTourStep as unknown as jest.Mock; + +describe('PanelHeader', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should render tab name', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: false }); + const { getByText } = render( + + ); + expect(GuidedOnboardingTourStep).not.toBeCalled(); + expect(getByText('Overview')).toBeInTheDocument(); + }); + + it('should render event header title when isAlert equals false', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: false }); + const { queryByTestId } = render( + + ); + expect(queryByTestId('alert-header')).not.toBeInTheDocument(); + expect(queryByTestId('event-header')).toBeInTheDocument(); + }); + + it('should render alert header title when isAlert equals true', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: true }); + const { queryByTestId } = render( + + ); + expect(queryByTestId('alert-header')).toBeInTheDocument(); + expect(queryByTestId('event-header')).not.toBeInTheDocument(); + }); + + it('should render tab name with guided onboarding tour info', () => { + mockUseBasicDataFromDetailsData.mockReturnValue({ isAlert: true }); + render( + + ); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].isTourAnchor).toBe(true); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].step).toBe(3); + expect(mockGuidedOnboardingTourStep.mock.calls[0][0].tourId).toBe('alertsCases'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx index 70c27e18faa23..b85476d2679fc 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/header.tsx @@ -7,7 +7,7 @@ import { EuiSpacer, EuiTab } from '@elastic/eui'; import type { FC } from 'react'; -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import type { RightPanelPaths } from '.'; import type { RightPanelTabType } from './tabs'; import { FlyoutHeader } from '../../shared/components/flyout_header'; @@ -16,6 +16,12 @@ import { AlertHeaderTitle } from './components/alert_header_title'; import { EventHeaderTitle } from './components/event_header_title'; import { useRightPanelContext } from './context'; import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers'; +import { + AlertsCasesTourSteps, + getTourAnchor, + SecurityStepId, +} from '../../../common/components/guided_onboarding_tour/tour_config'; +import { GuidedOnboardingTourStep } from '../../../common/components/guided_onboarding_tour/tour_step'; export interface PanelHeaderProps { /** @@ -38,16 +44,48 @@ export const PanelHeader: FC = memo( const { dataFormattedForFieldBrowser } = useRightPanelContext(); const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser); const onSelectedTabChanged = (id: RightPanelPaths) => setSelectedTabId(id); - const renderTabs = tabs.map((tab, index) => ( - onSelectedTabChanged(tab.id)} - isSelected={tab.id === selectedTabId} - key={index} - data-test-subj={tab['data-test-subj']} - > - {tab.name} - - )); + + const tourAnchor = useMemo( + () => + isAlert + ? { + 'tour-step': getTourAnchor( + AlertsCasesTourSteps.reviewAlertDetailsFlyout, + SecurityStepId.alertsCases + ), + } + : {}, + [isAlert] + ); + + const renderTabs = tabs.map((tab, index) => + isAlert && tab.id === 'overview' ? ( + + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + key={index} + data-test-subj={tab['data-test-subj']} + {...tourAnchor} + > + {tab.name} + + + ) : ( + onSelectedTabChanged(tab.id)} + isSelected={tab.id === selectedTabId} + key={index} + data-test-subj={tab['data-test-subj']} + > + {tab.name} + + ) + ); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx index 9cc2e246d94da..0f59727007703 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/cases_table/cases_table.test.tsx @@ -25,6 +25,8 @@ jest.mock('../../../../common/lib/kibana/hooks', () => { }; }); +jest.mock('../../../../common/components/guided_onboarding_tour/tour_step'); + type UseCaseItemsReturn = ReturnType; const defaultCaseItemsReturn: UseCaseItemsReturn = { items: [], diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx index b12fdb34e7f39..e48dbe5215f39 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/flyout/footer.test.tsx @@ -96,6 +96,7 @@ jest.mock( } ); jest.mock('../../../../../detections/components/alerts_table/actions'); +jest.mock('../../../../../common/components/guided_onboarding_tour/tour_step'); const defaultProps = { scopeId: TimelineId.test, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx index f64f3cf5fd73d..9d4264dd5b079 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.test.tsx @@ -42,7 +42,7 @@ jest.mock('../../../../../common/components/user_privileges', () => { }), }; }); - +jest.mock('../../../../../common/components/guided_onboarding_tour/tour_step'); jest.mock('../../../../../common/lib/kibana', () => { const originalModule = jest.requireActual('../../../../../common/lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 57ba0234f0c78..eb8aa54168461 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -40,6 +40,7 @@ import type { } from '@hello-pangea/dnd'; jest.mock('../../../../common/hooks/use_app_toasts'); +jest.mock('../../../../common/components/guided_onboarding_tour/tour_step'); jest.mock( '../../../../detections/components/alerts_table/timeline_actions/use_add_to_case_actions' ); From b1891e4c47b5ef60e61b486bb861f8ca381b1389 Mon Sep 17 00:00:00 2001 From: Tre Date: Tue, 28 May 2024 12:01:29 +0100 Subject: [PATCH 30/59] [FTR](management && painless_lab) update common serverless api tests to use api keys (#184304) ## Summary Use api keys for api calls, to act as the user, within: `x-pack/test_serverless/api_integration/test_suites/common/management/` && `x-pack/test_serverless/api_integration/test_suites/common/painless_lab/` Contributes to: https://github.com/elastic/kibana/issues/180834 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../common/management/ingest_pipelines.ts | 108 ++++++++++-------- .../test_suites/common/management/rollups.ts | 16 ++- .../common/management/scripted_fields.ts | 15 ++- .../test_suites/common/management/spaces.ts | 16 +-- .../common/painless_lab/painless_lab.ts | 17 ++- 5 files changed, 106 insertions(+), 66 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts index 0f4866f3c3a22..cf6750f91247d 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/ingest_pipelines.ts @@ -8,24 +8,34 @@ import expect from '@kbn/expect'; import { IngestPutPipelineRequest } from '@elastic/elasticsearch/lib/api/types'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const ingestPipelines = getService('ingestPipelines'); const log = getService('log'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('Ingest Pipelines', function () { + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); + }); after(async () => { await ingestPipelines.api.deletePipelines(); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); describe('Create', () => { it('should create a pipeline', async () => { const pipelineRequestBody = ingestPipelines.fixtures.createPipelineBody(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody); expect(body).to.eql({ @@ -36,10 +46,10 @@ export default function ({ getService }: FtrProviderContext) { it('should create a pipeline with only required fields', async () => { const pipelineRequestBody = ingestPipelines.fixtures.createPipelineBodyWithRequiredFields(); // Includes name and processors[] only - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody) .expect(200); @@ -56,10 +66,10 @@ export default function ({ getService }: FtrProviderContext) { await ingestPipelines.api.createPipeline({ id: name, ...esPipelineRequestBody }); // Then, create a pipeline with our internal API - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send(pipelineRequestBody) .expect(409); @@ -94,10 +104,10 @@ export default function ({ getService }: FtrProviderContext) { it('should allow an existing pipeline to be updated', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ ...pipeline, description: 'updated test pipeline description', @@ -116,10 +126,10 @@ export default function ({ getService }: FtrProviderContext) { it('should allow optional fields to be removed', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ // removes description, version, on_failure, and _meta processors: pipeline.processors, @@ -134,10 +144,10 @@ export default function ({ getService }: FtrProviderContext) { it('should not allow a non-existing pipeline to be updated', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/pipeline_does_not_exist`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .put(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ ...pipeline, description: 'updated test pipeline description', @@ -179,10 +189,10 @@ export default function ({ getService }: FtrProviderContext) { describe('all pipelines', () => { it('should return an array of pipelines', async () => { - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(ingestPipelines.fixtures.apiBasePath) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(Array.isArray(body)).to.be(true); @@ -202,10 +212,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return a single pipeline', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineName}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -244,10 +254,10 @@ export default function ({ getService }: FtrProviderContext) { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineA}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -263,10 +273,10 @@ export default function ({ getService }: FtrProviderContext) { const { body: { itemsDeleted, errors }, - } = await supertest + } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(errors).to.eql([]); @@ -283,10 +293,10 @@ export default function ({ getService }: FtrProviderContext) { const uri = `${ingestPipelines.fixtures.apiBasePath}/${pipelineD},${PIPELINE_DOES_NOT_EXIST}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .delete(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -315,10 +325,10 @@ export default function ({ getService }: FtrProviderContext) { it('should successfully simulate a pipeline', async () => { const { name, ...pipeline } = ingestPipelines.fixtures.createPipelineBody(); const documents = ingestPipelines.fixtures.createDocuments(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/simulate`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ pipeline, documents, @@ -334,10 +344,10 @@ export default function ({ getService }: FtrProviderContext) { const { name, ...pipeline } = ingestPipelines.fixtures.createPipelineBodyWithRequiredFields(); const documents = ingestPipelines.fixtures.createDocuments(); - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/simulate`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ pipeline, documents, @@ -380,10 +390,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return a document', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/documents/${INDEX}/${DOCUMENT_ID}`; - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(200); expect(body).to.eql({ @@ -396,10 +406,10 @@ export default function ({ getService }: FtrProviderContext) { it('should return an error if the document does not exist', async () => { const uri = `${ingestPipelines.fixtures.apiBasePath}/documents/${INDEX}/2`; // Document 2 does not exist - const { body } = await supertest + const { body } = await supertestWithoutAuth .get(uri) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .expect(404); expect(body).to.eql({ @@ -415,10 +425,10 @@ export default function ({ getService }: FtrProviderContext) { it('should map to a pipeline', async () => { const validCsv = 'source_field,copy_action,format_action,timestamp_format,destination_field,Notes\nsrcip,,,,source.address,Copying srcip to source.address'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${ingestPipelines.fixtures.apiBasePath}/parse_csv`) - .set('kbn-xsrf', 'xxx') - .set('x-elastic-internal-origin', 'xxx') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ copyAction: 'copy', file: validCsv, diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts index ce413ce5a7f3a..727b8b9abc927 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/rollups.ts @@ -12,13 +12,20 @@ import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common/src/con import { FIELDS_FOR_WILDCARD_PATH as BASE_URI } from '@kbn/data-views-plugin/common/constants'; import { DataViewType } from '@kbn/data-views-plugin/common'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('rollup data views - fields for wildcard', function () { before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index'); }); @@ -26,9 +33,10 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload( 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' ); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); it('returns 200 and best effort response despite lack of rollup support', async () => { - const response = await supertest + const response = await supertestWithoutAuth .get(BASE_URI) .query({ pattern: 'basic_index', @@ -36,7 +44,9 @@ export default function ({ getService }: FtrProviderContext) { rollup_index: 'bar', }) .set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL) - .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true'); + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader); expect(response.status).toBe(200); expect(response.body.fields.length).toEqual(5); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts index 0f17561ea7edc..af41fd61cc015 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/scripted_fields.ts @@ -8,14 +8,21 @@ import expect from 'expect'; import { DATA_VIEW_PATH } from '@kbn/data-views-plugin/server'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { InternalRequestHeader, RoleCredentials } from '../../../../shared/services'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; + let internalReqHeader: InternalRequestHeader; describe('scripted fields disabled', function () { before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + internalReqHeader = svlCommonApi.getInternalRequestHeader(); // TODO: We're running into a 'Duplicate data view: basic_index' // error in Serverless, so make sure to clean up first await kibanaServer.savedObjects.cleanStandardList(); @@ -26,11 +33,13 @@ export default function ({ getService }: FtrProviderContext) { await esArchiver.unload( 'test/api_integration/fixtures/es_archiver/index_patterns/basic_index' ); + await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); it('scripted fields are ignored when disabled', async () => { - const response = await supertest + const response = await supertestWithoutAuth .post(DATA_VIEW_PATH) - .set('kbn-xsrf', 'some-xsrf-token') + .set(internalReqHeader) + .set(roleAuthc.apiKeyHeader) .send({ data_view: { title: 'basic_index', diff --git a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts index a93cdd048d21f..19cacb01641dc 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/management/spaces.ts @@ -34,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) { it('#delete', async () => { const { body, status } = await supertestWithoutAuth .delete('/api/spaces/space/default') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // normally we'd get a 400 bad request if we tried to delete the default space @@ -44,7 +44,7 @@ export default function ({ getService }: FtrProviderContext) { it('#create', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/space') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader) .send({ id: 'custom', @@ -58,7 +58,7 @@ export default function ({ getService }: FtrProviderContext) { it('#update requires internal header', async () => { const { body, status } = await supertestWithoutAuth .put('/api/spaces/space/default') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader) .send({ id: 'default', @@ -72,7 +72,7 @@ export default function ({ getService }: FtrProviderContext) { it('#copyToSpace', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_copy_saved_objects') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -82,7 +82,7 @@ export default function ({ getService }: FtrProviderContext) { it('#resolveCopyToSpaceErrors', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_resolve_copy_saved_objects_errors') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -92,7 +92,7 @@ export default function ({ getService }: FtrProviderContext) { it('#updateObjectsSpaces', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_update_objects_spaces') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -102,7 +102,7 @@ export default function ({ getService }: FtrProviderContext) { it('#getShareableReferences', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_get_shareable_references') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered @@ -112,7 +112,7 @@ export default function ({ getService }: FtrProviderContext) { it('#disableLegacyUrlAliases', async () => { const { body, status } = await supertestWithoutAuth .post('/api/spaces/_disable_legacy_url_aliases') - .set(commonRequestHeader) + .set(internalRequestHeader) .set(roleAuthc.apiKeyHeader); // without a request body we would normally a 400 bad request if the endpoint was registered diff --git a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts index 1d0d5cefedb86..122b1d9c87916 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/painless_lab/painless_lab.ts @@ -7,22 +7,32 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; +import { RoleCredentials } from '../../../../shared/services'; const API_BASE_PATH = '/api/painless_lab'; export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertestWithoutAuth = getService('supertestWithoutAuth'); + let roleAuthc: RoleCredentials; describe('Painless Lab Routes', function () { + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + }); + after(async () => { + await svlUserManager.invalidateApiKeyForRole(roleAuthc); + }); describe('Execute', () => { it('should execute a valid painless script', async () => { const script = '"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) .set('Content-Type', 'application/json;charset=UTF-8') .send(script) .expect(200); @@ -36,10 +46,11 @@ export default function ({ getService }: FtrProviderContext) { const invalidScript = '"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"'; - const { body } = await supertest + const { body } = await supertestWithoutAuth .post(`${API_BASE_PATH}/execute`) .set(svlCommonApi.getInternalRequestHeader()) .set('Content-Type', 'application/json;charset=UTF-8') + .set(roleAuthc.apiKeyHeader) .send(invalidScript) .expect(200); From df910f7725872f10baf4955eb4e240b43d6beab2 Mon Sep 17 00:00:00 2001 From: Joe McElroy Date: Tue, 28 May 2024 12:18:57 +0100 Subject: [PATCH 31/59] [Search] [Playground] Improve model_id detection (#184261) ## Summary In order to perform semantic search, we need to detect the model_id used by the inference processor. We do this currently by performing a search for a single document and looking up the values in the source. The issue is that some documents may have failed to be infered or where the field is missing. This means there is a chance where the model_id is missing from the source and we are unable to detect it. This change performs a term aggregation on the model_id fields instead. ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fetch_query_source_fields.mock.ts | 911 +++++------------- .../lib/fetch_query_source_fields.test.ts | 95 +- .../server/lib/fetch_query_source_fields.ts | 154 ++- 3 files changed, 412 insertions(+), 748 deletions(-) diff --git a/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts b/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts index ecdb85dfaa445..355494b4a217e 100644 --- a/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts +++ b/x-pack/plugins/search_playground/__mocks__/fetch_query_source_fields.mock.ts @@ -5,72 +5,135 @@ * 2.0. */ +import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; + export const ELSER_PASSAGE_CHUNKED_TWO_INDICES_DOCS = [ { - _index: 'workplace_index', - _id: '248629d8-64d7-4e91-a4eb-dbd8282d9f24', - _score: 1, - _ignored: ['metadata.summary.keyword', 'text.keyword'], - _source: { - metadata: { - summary: 'This policy', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - }, - vector: { - tokens: {}, - model_id: '.elser_model_2', - }, - text: 'Effective: March 2020', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'vector.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, { - _index: 'workplace_index2', - _id: 'b047762c-24eb-4846-aeb5-808346d54c54', - _score: 1, - _ignored: ['content.keyword', 'metadata.summary.keyword'], - _source: { - metadata: { - summary: - 'This policy outlines the guidelines for full-time remote work, including eligibility, equipment and resources, workspace requirements, communication expectations, performance expectations, time tracking and overtime, confidentiality and data security, health and well-being, and policy reviews and updates. Employees are encouraged to direct any questions or concerns', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - }, - content: 'Effective', - content_vector: { - tokens: {}, - model_id: '.elser_model_2', - }, - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'content_vector.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const DENSE_INPUT_OUTPUT_ONE_INDEX = [ { - _index: 'index2', - _id: 'KQ6Wco8BO787m9kIp1Ug', - _score: 1, - _source: { - text_embedding: [0.03889123350381851], - text: 'hello there', - model_id: '.multilingual-e5-small', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + model_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.multilingual-e5-small', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const SPARSE_INPUT_OUTPUT_ONE_INDEX = [ { - _index: 'index', - _id: 'Iw6Bco8BO787m9kIa1Wo', - _score: 1, - _source: { - text_embedding: { - interview: 0.42307013, - }, - text: 'hello there', - model_id: '.elser_model_2', - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + model_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.elser_model_2', + doc_count: 1, + }, + ], + }, + }, + } as SearchResponse, ]; export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { @@ -111,6 +174,36 @@ export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { }, }; +export const SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD = { + indices: ['index'], + fields: { + text_embedding: { + sparse_vector: { + type: 'sparse_vector', + metadata_field: false, + searchable: true, + aggregatable: false, + }, + }, + text: { + text: { + type: 'text', + metadata_field: false, + searchable: true, + aggregatable: false, + }, + }, + model_id: { + keyword: { + type: 'keyword', + metadata_field: false, + searchable: true, + aggregatable: true, + }, + }, + }, +}; + export const DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { indices: ['index2'], fields: { @@ -151,572 +244,23 @@ export const DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS = { export const DENSE_VECTOR_DOCUMENT_FIRST = [ { - _index: 'workplace_index_nested', - _id: 'MXEeQo4BweykRPD22e0N', - _score: 1, - _ignored: ['content.keyword', 'metadata.summary.keyword', 'metadata.content.keyword'], - _source: { - metadata: { - summary: - 'This policy outlines the guidelines for full-time remote work, including eligibility, equipment and resources, workspace requirements, communication expectations, performance expectations, time tracking and overtime, confidentiality and data security, health and well-being, and policy reviews and updates. Employees are encouraged to direct any questions or concerns', - _run_ml_inference: true, - updated_at: '2020-03-01', - created_on: '2020-03-01', - rolePermissions: ['demo', 'manager'], - name: 'Work From Home Policy', - category: 'teams', - content: `Effective: March 2020 -Purpose - -The purpose of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic and beyond. -Scope - -This policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the office. -Eligibility - -Employees who can perform their work duties remotely and have received approval from their direct supervisor and the HR department are eligible for this work-from-home arrangement. -Equipment and Resources - -The necessary equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and data. -Workspace - -Employees working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from distractions. -Communication - -Effective communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication tools. -Work Hours and Availability - -Employees are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR department. -Performance Expectations - -Employees working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote work. -Time Tracking and Overtime - -Employees are required to accurately track their work hours using the company's time tracking system. Non-exempt employees must obtain approval from their supervisor before working overtime. -Confidentiality and Data Security - -Employees must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT department. -Health and Well-being - -The company encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when needed. -Policy Review and Updates - -This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback. -Questions and Concerns - -Employees are encouraged to direct any questions or concerns about this policy to their supervisor or the HR department. -`, - url: './sharepoint/Work from home policy.txt', - }, - passages: [ - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Effective: March 2020 Purpose', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The purpose of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'of this full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'full-time work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work-from-home policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policy is to provide guidelines and support for employees to conduct their work remotely, ensuring the continuity and productivity of business operations during the COVID-19 pandemic and beyond.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Scope', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'This policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policy applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'applies to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to all employees who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'who are eligible for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'remote work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'as determined by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'by their role and responsibilities. It is designed to allow employees to work from home full time while maintaining the same level of performance and collaboration as they would in the office.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Eligibility', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees who can perform their work duties remotely and have received approval from their direct supervisor and the HR department are eligible for this work-from-home arrangement.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Equipment and Resources', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The necessary equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'equipment and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'and resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'resources will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'will be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'be provided to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "to employees for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "for remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "remote work, including a company-issued laptop, software licenses, and access to secure communication tools. Employees are responsible for maintaining and protecting the company's equipment and data.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Workspace', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'working from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'from home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'home are responsible for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for creating a comfortable and safe workspace that is conducive to productivity. This includes ensuring that their home office is ergonomically designed, well-lit, and free from distractions.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Communication', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Effective communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls,', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'communication is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'is vital for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'for successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'successful remote work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'work. Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are expected to maintain regular communication with their supervisors, colleagues, and team members through email, phone calls, video conferences, and other approved communication tools.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Work Hours and Availability', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'are expected to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'maintain their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'their regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "regular work hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "hours and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "and be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "be available during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "during normal business hours, unless otherwise agreed upon with their supervisor. Any changes to work hours or availability must be communicated to the employee's supervisor and the HR department.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Performance Expectations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'working from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'from home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'home are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'are expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'expected to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'maintain the same level of performance and productivity as if they were working in the office. Supervisors and team members will collaborate to establish clear expectations and goals for remote work.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Time Tracking and Overtime', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "Employees are required to accurately track their work hours using the company's time tracking system. Non-exempt employees must obtain approval from their supervisor before working overtime.", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Confidentiality and Data Security', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "Employees must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "must adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections,", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "adhere to the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "the company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: "company's confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any", - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'confidentiality and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'and data security policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'policies while working from home. This includes safeguarding sensitive information, securing personal devices and internet connections, and reporting any security breaches to the IT department.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Health and Well-being', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'The company encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'encourages employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'employees to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'to prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'prioritize their health and well-being while working from home. This includes taking regular breaks, maintaining a work-life balance, and seeking support from supervisors and colleagues when needed.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Policy Review and Updates', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback.', - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: `This work-from-home policy will be reviewed periodically and updated as necessary, taking into account changes in public health guidance, business needs, and employee feedback. -Questions and Concerns`, - }, - { - vector: { - model_id: '.multilingual-e5-small', - }, - text: 'Employees are encouraged to direct any questions or concerns about this policy to their supervisor or the HR department.', - }, - ], - }, - }, + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + } as SearchResponse, ]; export const DENSE_VECTOR_DOCUMENT_FIRST_FIELD_CAPS = { @@ -1258,51 +802,35 @@ export const ELSER_PASSAGE_CHUNKED = { }; export const SPARSE_DOC_SINGLE_INDEX = { - _index: 'search-nethys', - _id: '662812de44a5e12074edcc6f', - _score: 1, - _ignored: ['body_content.enum'], - _source: { - last_crawled_at: '2024-04-23T20:05:14Z', - additional_urls: ['https://2e.aonprd.com/Ancestries.aspx?ID=31'], - body_content: - "Ancestry Guide pg. 89 2.0 Fleshwarps are people whose forms were created or radically transformed by magic, alchemy, or unnatural energies. Their unorthodox appearance can make it difficult for them to find a place for themselves in the world. Magic and science that can warp bone and twist sinew are all too common on Golarion. Fleshwarps are those who have been permanently altered by such methods—sometimes a sapient being created whole cloth from inanimate flesh, but often a victim unwillingly transformed by strange energies or sadistic creators. The ancestry name “fleshwarp” is an umbrella term, since on Golarion the actual fleshwarping process is more infamously well-known than are alterations caused by uncontrolled magic, technology, or fringe science. Whether practiced by Thassilonian wizards, Nexian fleshcrafters, or the drow of the Darklands, fleshwarping is the craft of reshaping flesh and mind in vats of foul magical reagents. This has led some scholars of monsters to argue that only those beings created by traditional fleshwarping should be considered fleshwarps. Regardless of the source of their altered forms, fleshwarps bear their new shape forever, transformed beings living a wild and strange existence beyond what was possible for their original ancestry. Although fleshwarps are humanoid, no two look the same. One might possess limbs in unusual places and skin as smooth as glass, while another might have a thick matting of spiny fur. Some might have animalistic features, like a boar snout, scales, or cloven hooves. Others have entirely alien appearances, such as bulging eyes on the backs of their hands. Some may have only subtly uncanny features that differentiate them, such as glowing teeth, smoking eyes, or fingernails made of bone. The only commonality among fleshwarps is their mismatched nature. Let your imagination run wild when creating a fleshwarp character! If you want a character who is tough and hardy, can change their form as they grow, and can use their wholly unique appearance to inspire awe or fear in others, you should play a fleshwarp. You Might... Embrace your unusual appearance to inspire respect or fear. Be used to relying on yourself. Distrust large groups of people, particularly mobs, based on past experiences. Others Probably... Find your physiology fascinating or terrifying. Assume you are an expert on strange creatures or occult phenomena. Consider you an enigmatic and unpredictable—and perhaps even dangerous—outsider. Physical Description Fleshwarps are humanoids, ranging from 5 to 7 feet tall and from just under 100 pounds to more than 300 pounds. The proportion and appearance of their limbs and features differ widely, but fleshwarps functionally have two legs, two arms, and a single head; a fleshwarp with more limbs than this should consider an appropriate ancestry feat to reflect this variance, or one of their limbs might be vestigial and mostly nonfunctional. Fleshwarps differ widely in their appearance due to the unique circumstances of their creation. Even fleshwarp siblings or two people transformed through the same procedure might look wildly different. Society Fleshwarps are so few in number that congregations of them are rare. They most often live on their own, with a small family group, or at the outskirts of a community. Some thrive in cities, however, where they can remain anonymous among the crowds while pursuing careers that allow them to avoid contact with people who might fear or persecute them. Fleshwarps value endurance and are quick to learn from others, so those who come into contact with others of their kind usually share stories that help each other survive, hide, or thrive more effectively. How a fleshwarp formed can be a painful or horrifying subject, one they consider rude to discuss with anyone besides close friends or loved ones. Alignment and Religion Fleshwarps have little to gain from the broader society, and therefore rarely work to support society in turn, beyond perhaps helping other fleshwarps. They need to be able to adapt quickly to survive on their own. As a consequence, few fleshwarps are lawful. Although bigoted or short-sighted people view fleshwarps as monsters, fleshwarps are no more or less prone to evil than any other people, and most seek only to live their lives without trouble. Most are neutral in alignment, for while alienation doesn't force a fleshwarp to feel contempt for others, neither does it encourage a fleshwarp to avoid it. This is especially true for fleshwarps living in the societies that gave birth to their traumatic transformation. Fleshwarps aren't often casually religious; most either have little to do with faith at all (viewing themselves as scorned by the gods or simply seeing faith as impractical for survival) or are exceptionally devout. Religious fleshwarps often revere Arazni , Calistria , Desna , or Gozreh ; evil fleshwarps typically turn to Lamashtu , finding consolation in the Mother of Monsters. Adventurers Fleshwarps often live on the margins of society. The hermit , hunter , nomad , or street urchin backgrounds work well for many fleshwarps; others might be criminals , entertainers , or prisoners . The need to defend themselves leads many fleshwarps to become barbarians , fighters , rogues , or rangers . champions and druids are common callings among fleshwarps who seek to defend and better the lot of others of their kind. Names Fleshwarps can come from—and thus have names from—any culture or ancestry, but some give themselves new names after being transformed, whether to celebrate the change, recognize a new phase of their lives, or conceal their past identity. Many fleshwarps also carry a descriptive nickname granted to them by others, such as “Triple Handed,” “Barkfoot,” or “Many-Mouth.” Fleshwarps don't keep nicknames they find personally offensive, but they tend to keep ones that describe their distinctive appearances or that are given by people they care about. Sample Names Borble, Dag, Feff, Hurn, Kemp, Omber, Ostro, Shurni, Surm, Wumpin Other Information Fleshforges In the city of Ecanus, the archmage Nex created the Fleshforges—massive edifices that churn and tremble with the birth sequences' roar of smelting flesh and printing bone—to produce fleshwarp soldiers to fuel his war against Geb. The Fleshforgers still engineer a bedazzling array of fleshwarps here, from chimeric messengers, who meld house pet and golem, to disciplined scale-sheathed cataphracts and stupendous dreadnoughts, whose fists and footsteps bend steel and pulverize stone. Recently, the Fleshforges have experienced uninitiated activations with alarming frequency, delivering atypical fleshwarps unconfined by design purviews or production schedules. Ecanus's authorities fervently hunt these unlicensed creations, but some escape, lurching into the night to hide in the city's recesses or venturing afield into the Mana Wastes. Fleshwarp Legends Legends of famous fleshwarps travel quickly amid fleshwarp communities. Lady Kedley : A wealthy noble in a life she doesn't remember, Kedley emerged transformed from deep below Westcrown. She uses her family's vast fortune to aid other fleshwarps. Spinhead Vanluk : This Mana Wastes warlord brings mutants and fleshwarps under his banner. He (literally) has eyes in the back of his head. Fleshwarp Motives Fleshwarps have a variety of reasons for taking up the life of an adventurer. Some are turned out of their homes by an uncaring parent or a suspicious mob. Other fleshwarps travel to learn more about their own transformation or to seek out others of their kind. A few even actively seek the means—either technological or magical—to undo their transformations or adopt a new form that won't incite repulsion or fear in common people. Still others understand that a good way to earn respect is to solve a community's problems—then quickly leave the area—and therefore fall into the role of itinerant adventurers-for-hire. Fleshwarp Settlements No settlements consisting entirely of fleshwarps exist openly outside of the Mana Wastes; elsewhere, citizens keep the secret so well that their community's existence isn't known to the world at large. Fleshwarps are more likely to live on the fringes of other settlements, working in industries where their hardy constitution is an advantage and their uncanny appearance isn't a liability. Some are herbalists or trappers, working in the wilderness and interacting with others only rarely. Locals might come to consider a fleshwarp their equal or friend, and take umbrage at outsiders who make a big deal out of the “monster” in their midst. Sentiment might turn quickly upon supernatural events or strange attacks, however, and, tragically, more than a few fleshwarps have been turned out of homes they've occupied for decades when faced with a misguided mob. The Mana Wastes The Mana Wastes are the vast swaths of desolation adjoining once-warring Geb and Nex. Over more than a thousand years, both nations' unrestricted use of magical fusillades, creeping plagues, and other atrocities of spellcraft transformed a once fertile and beautiful land into a desert where reality screams and bleeds. Wellspring surges of magic form into riptides and whirlwinds of chaotic force throughout the Mana Wastes, rending and twisting everything in their path. Many fleshwarps make their homes in this inhospitable land. Some are native to the region, mutated by the Mana Wastes' volatile outbursts, while others are exiles and refugees from across the Impossible Lands who find the otherworldly hazards of the Mana Wastes preferable to the all-too-worldly persecution of their former homelands and compatriots. Fleshwarp Mechanics Hit Points 10 Size Medium or Small Speed 25 feet Ability Boosts Constitution Free Languages Common Additional languages equal to your Intelligence modifier (if positive). Choose from Aklo , Draconic , Dwarven , Elven , Goblin , Undercommon , and any other languages to which you have access (such as the languages prevalent in your region). Low-Light Vision You can see in dim light as though it were bright light, and you ignore the concealed condition due to dim light. Unusual Anatomy Your unorthodox body resists physical afflictions meant for other creatures. You gain a +1 circumstance bonus to saves against diseases and poisons .", - domains: ['https://2e.aonprd.com'], - title: 'Fleshwarp - Ancestries - Archives of Nethys: Pathfinder 2nd Edition Database', - meta_keywords: - 'Archives, Nethys, Wiki, Archives of Nethys, Pathfinder, Official, AoN, AoNPRD, PRD, PFSRD, 2E, 2nd Edition, Ancestries, Ancestry, Fleshwarp', - url: 'https://2e.aonprd.com/Ancestries.aspx?ID=31', - url_scheme: 'https', - meta_description: - 'Magic and science that can warp bone and twist sinew are all too common on Golarion. Fleshwarps are those who have been permanently altered by such methods—sometimes a sapient being created whole cloth from inanimate flesh, but often a victim unwillingly transformed by strange energies or sadistic creators.

The ancestry name “fleshwarp” is an umbrella term, since on Golarion the actual fleshwarping process is more infamously well-known than are alterations caused by uncontrolled magic, technology, or fringe science. Whether practiced by Thassilonian wizards, Nexian fleshcrafters, or the drow of the Darklands, fleshwarping is the craft of reshaping flesh and mind in vats of foul magical reagents. This has led some scholars of monsters to argue that only those beings created by traditional fleshwarping should be considered fleshwarps. Regardless of the source of their altered forms, fleshwarps bear their new shape forever, transformed beings living a wild and strange existence beyond what w…', - _ingest: { - processors: [ + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'ml.inference.body_content_expanded.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ { - pipeline: 'ml.inference.nethys', - model_version: '12.0.0', - types: ['pytorch', 'text_expansion'], - processed_timestamp: '2024-04-23T20:05:15.723072191Z', + key: '.elser_model_2_linux-x86_64', + doc_count: 1, }, ], }, - headings: ['Alchemist'], - links: ['https://2e.aonprd.com/Causes.aspx'], - id: '662812de44a5e12074edcc6f', - url_port: 443, - url_host: '2e.aonprd.com', - url_path: '/Ancestries.aspx', - url_path_dir1: 'Ancestries.aspx', - ml: { - inference: { - body_content_expanded: { - predicted_value: {}, - is_truncated: true, - model_id: '.elser_model_2_linux-x86_64', - }, - }, - }, }, -}; +} as SearchResponse; export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS = { indices: ['search-example-main'], @@ -1663,37 +1191,32 @@ export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS = { }; export const DENSE_PASSAGE_FIRST_SINGLE_INDEX_DOC = { - _index: 'search-example-main', - _id: 'id', - _version: 1, - _seq_no: 2, - _primary_term: 1, - found: true, - _source: { - page_notification: '-', - 'main_button.button_new_tab': '-', - page_content_key: '', - label: '', - bread_crumbs: 'breadcrumbs', - title: 'title', - type: '11', - url: '/', - page_content_text: 'page_content_text', - page_id: '2,061', - 'buttons.button_title': '-', - page_content_e5_embbeding: { - predicted_value: [0.09232209622859955], - model_id: '.multilingual-e5-small_linux-x86_64', - }, - category_id: 'category_id', - filter_list: 'filter', - 'buttons.button_link': '-', - 'buttons.button_new_tab': '-', - 'main_button.button_title': '-', - title_text: 'title_text', - 'main_button.button_link': '-', - page_content: 'bla', - updated_date: '2024-03-21T11:23:12.503000', - title_keyword: 'title_keyword', + took: 0, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, }, -}; + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + 'page_content_e5_embbeding.model_id': { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '.multilingual-e5-small_linux-x86_64', + doc_count: 1, + }, + ], + }, + }, +} as SearchResponse; diff --git a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts index 70b640f1ea70d..108ddabb0a73f 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.test.ts @@ -18,8 +18,13 @@ import { DENSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS, SPARSE_INPUT_OUTPUT_ONE_INDEX, SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS, + SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD, } from '../../__mocks__/fetch_query_source_fields.mock'; -import { parseFieldsCapabilities } from './fetch_query_source_fields'; +import { + fetchFields, + getModelIdFields, + parseFieldsCapabilities, +} from './fetch_query_source_fields'; describe('fetch_query_source_fields', () => { describe('parseFieldsCapabilities', () => { @@ -252,4 +257,92 @@ describe('fetch_query_source_fields', () => { }); }); }); + + describe('getModelIdFields', () => { + it('should return the model_id field for field specific - dense', () => { + expect(getModelIdFields(DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS)).toEqual([ + { + aggField: 'page_content_e5_embbeding.model_id.keyword', + path: 'page_content_e5_embbeding.model_id', + }, + { aggField: 'page_content_ner.model_id', path: 'page_content_ner.model_id' }, + ]); + }); + + it('should return the model_id field for field specific - elser', () => { + expect(getModelIdFields(DENSE_VECTOR_DOCUMENT_FIRST_FIELD_CAPS)).toEqual([ + { aggField: 'passages.vector.model_id.keyword', path: 'passages.vector.model_id' }, + ]); + }); + + it('should return top level model_id', () => { + expect(getModelIdFields(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS)).toEqual([ + { aggField: 'model_id.keyword', path: 'model_id' }, + ]); + }); + + it('should return the model_id as aggField if its a keyword field', () => { + expect(getModelIdFields(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS_MODEL_ID_KEYWORD)).toEqual([ + { aggField: 'model_id', path: 'model_id' }, + ]); + }); + }); + + describe('fetchFields', () => { + it('should perform a search request with the correct parameters', async () => { + const client = { + asCurrentUser: { + fieldCaps: jest.fn().mockResolvedValue(DENSE_PASSAGE_FIRST_SINGLE_INDEX_FIELD_CAPS), + search: jest.fn().mockResolvedValue(DENSE_PASSAGE_FIRST_SINGLE_INDEX_DOC), + }, + } as any; + const indices = ['search-example-main']; + await fetchFields(client, indices); + expect(client.asCurrentUser.search).toHaveBeenCalledWith({ + index: 'search-example-main', + body: { + size: 0, + aggs: { + 'page_content_e5_embbeding.model_id': { + terms: { + field: 'page_content_e5_embbeding.model_id.keyword', + size: 1, + }, + }, + 'page_content_ner.model_id': { + terms: { + field: 'page_content_ner.model_id', + size: 1, + }, + }, + }, + }, + }); + }); + + it('should perform a search request with the correct parameters with top level model id', async () => { + const client = { + asCurrentUser: { + fieldCaps: jest.fn().mockResolvedValue(SPARSE_INPUT_OUTPUT_ONE_INDEX_FIELD_CAPS), + search: jest.fn().mockResolvedValue(SPARSE_INPUT_OUTPUT_ONE_INDEX), + }, + } as any; + const indices = ['index']; + await fetchFields(client, indices); + expect(client.asCurrentUser.search).toHaveBeenCalledWith({ + index: 'index', + body: { + size: 0, + aggs: { + model_id: { + terms: { + field: 'model_id.keyword', + size: 1, + }, + }, + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts index 6bea3f8f03978..b333a0e95962b 100644 --- a/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts +++ b/x-pack/plugins/search_playground/server/lib/fetch_query_source_fields.ts @@ -5,11 +5,51 @@ * 2.0. */ -import { FieldCapsResponse, SearchHit } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SearchResponse, FieldCapsResponse } from '@elastic/elasticsearch/lib/api/types'; import { IScopedClusterClient } from '@kbn/core-elasticsearch-server'; -import { get, has } from 'lodash'; import { IndicesQuerySourceFields } from '../types'; +interface FieldModelId { + field: string; + modelId: string | undefined; +} + +interface IndexFieldModel { + index: string; + fields: FieldModelId[]; +} + +export const getModelIdFields = (fieldCapsResponse: FieldCapsResponse) => { + const { fields } = fieldCapsResponse; + return Object.keys(fields).reduce>((acc, fieldKey) => { + const field = fields[fieldKey]; + if (fieldKey.endsWith('model_id')) { + if ('keyword' in field && field.keyword.aggregatable) { + acc.push({ + path: fieldKey, + aggField: fieldKey, + }); + return acc; + } + const keywordModelIdField = fields[fieldKey + '.keyword']; + + if ( + keywordModelIdField && + `keyword` in keywordModelIdField && + keywordModelIdField.keyword.aggregatable + ) { + acc.push({ + path: fieldKey, + aggField: fieldKey + '.keyword', + }); + return acc; + } + } + + return acc; + }, []); +}; + export const fetchFields = async ( client: IScopedClusterClient, indices: string[] @@ -21,56 +61,51 @@ export const fetchFields = async ( index: indices, }); - const indexDocs = []; + const modelIdFields = getModelIdFields(fieldCapabilities); - for (const index of indices) { - const x = await client.asCurrentUser.search({ + const indicesAggs = await Promise.all( + indices.map(async (index) => ({ index, - body: { - query: { - match_all: {}, - }, - size: 1, - }, - }); - - if (x.hits.total !== 0) { - indexDocs.push({ + doc: await client.asCurrentUser.search({ index, - doc: x.hits.hits[0], - }); - } - } + body: { + size: 0, + aggs: modelIdFields.reduce( + (sum, modelIdField) => ({ + ...sum, + [modelIdField.path]: { + terms: { + field: modelIdField.aggField, + size: 1, + }, + }, + }), + {} + ), + }, + }), + })) + ); - return parseFieldsCapabilities(fieldCapabilities, indexDocs); + return parseFieldsCapabilities(fieldCapabilities, indicesAggs); }; const INFERENCE_MODEL_FIELD_REGEXP = /\.predicted_value|\.tokens/; -const hasModelField = (field: string, indexDoc: any, nestedField: string | false) => { - if (field.match(INFERENCE_MODEL_FIELD_REGEXP)) { - const path = nestedField ? field.replace(`${nestedField}.`, `${nestedField}[0].`) : field; - return has( - indexDoc.doc, - `_source.${[path.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id')]}` - ); - } - return false; -}; - -// For input_output inferred fields, the model_id is at the top level -const hasTopLevelModelField = (indexDoc: any) => { - return has(indexDoc.doc, `_source.model_id`); -}; +const getModelField = (field: string, modelIdFields: FieldModelId[]) => { + // For input_output inferred fields, the model_id is at the top level + const topLevelModelField = modelIdFields.find( + (modelIdField) => modelIdField.field === 'model_id' + )?.modelId; -const getModelField = (field: string, indexDoc: any, nestedField: string | false) => { - if (hasTopLevelModelField(indexDoc)) { - return get(indexDoc.doc, `_source.model_id`); + if (topLevelModelField) { + return topLevelModelField; } - // If the field is nested, we need to get the first occurrence as its an array - const path = nestedField ? field.replace(`${nestedField}.`, `${nestedField}[0].`) : field; - return get(indexDoc.doc, `_source.${[path.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id')]}`); + return modelIdFields.find( + (modelIdField) => + modelIdField.field === field.replace(INFERENCE_MODEL_FIELD_REGEXP, '.model_id') + )?.modelId; }; const isFieldNested = (field: string, fieldCapsResponse: FieldCapsResponse) => { @@ -94,11 +129,25 @@ const isFieldNested = (field: string, fieldCapsResponse: FieldCapsResponse) => { export const parseFieldsCapabilities = ( fieldCapsResponse: FieldCapsResponse, - indexDocs: Array<{ index: string; doc: SearchHit }> + aggDocs: Array<{ index: string; doc: SearchResponse }> ): IndicesQuerySourceFields => { const { fields, indices: indexOrIndices } = fieldCapsResponse; const indices = Array.isArray(indexOrIndices) ? indexOrIndices : [indexOrIndices]; + const indexModelIdFields = aggDocs.map((aggDoc) => { + const modelIdFields = Object.keys(aggDoc.doc.aggregations || {}).map((field) => { + return { + field, + modelId: (aggDoc.doc.aggregations![field] as any)?.buckets?.[0]?.key, + }; + }); + + return { + index: aggDoc.index, + fields: modelIdFields, + }; + }); + const indicesFieldsMap = indices.reduce((acc, index) => { acc[index] = { elser_query_fields: [], @@ -125,20 +174,21 @@ export const parseFieldsCapabilities = ( : (indices as unknown as string[]); for (const index of indicesPresentIn) { - const indexDoc = indexDocs.find((x) => x.index === index); + const modelIdFields = indexModelIdFields.find( + (indexModelIdField) => indexModelIdField.index === index + )!.fields; + if ('rank_features' in field || 'sparse_vector' in field) { const nestedField = isFieldNested(fieldKey, fieldCapsResponse); + const modelId = getModelField(fieldKey, modelIdFields); // Check if the sparse vector field has a model_id associated with it // skip this field if has no model associated with it // and the vectors were embedded outside of stack - if ( - (hasModelField(fieldKey, indexDoc, nestedField) || hasTopLevelModelField(indexDoc)) && - !nestedField - ) { + if (modelId && !nestedField) { const elserModelField = { field: fieldKey, - model_id: getModelField(fieldKey, indexDoc, nestedField), + model_id: modelId, nested: !!isFieldNested(fieldKey, fieldCapsResponse), indices: indicesPresentIn, }; @@ -148,17 +198,15 @@ export const parseFieldsCapabilities = ( } } else if ('dense_vector' in field) { const nestedField = isFieldNested(fieldKey, fieldCapsResponse); + const modelId = getModelField(fieldKey, modelIdFields); // Check if the dense vector field has a model_id associated with it // skip this field if has no model associated with it // and the vectors were embedded outside of stack - if ( - (hasModelField(fieldKey, indexDoc, nestedField) || hasTopLevelModelField(indexDoc)) && - !nestedField - ) { + if (modelId && !nestedField) { const denseVectorField = { field: fieldKey, - model_id: getModelField(fieldKey, indexDoc, nestedField), + model_id: modelId, nested: !!nestedField, indices: indicesPresentIn, }; From 69b28f317b07af12fa346571b04b411a6130b25f Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Tue, 28 May 2024 13:45:25 +0200 Subject: [PATCH 32/59] Rule execution log support backfill rule run types (#183898) ## Rule execution log support backfill rule run types https://github.com/elastic/kibana/assets/7609147/38662629-d600-449b-949a-2aa0166ea3a1 ### Feature flag `manualRuleRunEnabled` ### Description - Add new column for table with rule run type "Manual" / "Scheduled" - Add new switch to show column with source event time range for backfill run - event execution log api support `run_type_filters` filters as parameter with values like "standard" and "backfill" - event execution log result will return new field for backfill runs - `backfill` ### How to test 1 . Enable feature flag - `manualRuleRunEnabled` 2. For you rule call schedule api `/internal/alerting/rules/backfill/_schedule` `POST` With this body (put your values for rule id and date range): ``` [{"rule_id":"58b4b926-6348-4c23-be1f-870a461fa342","start":"2024-05-21T13:00:00.000Z","end":"2024-05-21T14:05:00.000Z"}] ``` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../alerting_event_logger.test.ts | 10 +- .../alerting_event_logger.ts | 1 + .../detection_engine/rule_monitoring/index.ts | 1 + .../model/execution_result.gen.ts | 9 + .../model/execution_result.schema.yaml | 13 + .../model/execution_run_type.gen.ts | 25 ++ .../model/execution_run_type.schema.yaml | 14 + .../get_rule_execution_results_route.gen.ts | 5 + ...t_rule_execution_results_route.schema.yaml | 9 + .../get_rule_execution_results_route.test.ts | 1 + .../security_solution/common/constants.ts | 3 + .../rule_management/execution_log.ts | 20 ++ .../common/experimental_features.ts | 5 + .../public/common/translations.ts | 14 + .../__mocks__/rule_details_context.tsx | 4 + .../execution_log_search_bar.test.tsx.snap | 30 +- .../execution_log_columns.tsx | 45 +++ .../execution_log_search_bar.test.tsx | 6 + .../execution_log_search_bar.tsx | 56 ++-- .../execution_log_table.tsx | 68 ++++- .../execution_log_table/translations.ts | 28 ++ .../rule_details/rule_details_context.tsx | 26 +- .../rule_monitoring/api/api_client.ts | 2 + .../api/api_client_interface.ts | 6 + .../execution_run_type_filter/index.tsx | 52 ++++ .../execution_run_type_filter/translations.ts | 15 + .../use_execution_results.test.tsx | 4 + .../use_execution_results.tsx | 13 +- .../detection_engine/rule_monitoring/index.ts | 1 + .../get_rule_execution_results_route.ts | 2 + .../client_for_routes/client_interface.ts | 3 + .../execution_results/index.test.ts | 266 +++++++++++++++++- .../aggregations/execution_results/index.ts | 49 +++- .../aggregations/execution_results/types.ts | 3 + .../event_log/event_log_reader.ts | 169 ++++++++--- .../tests/alerting/backfill/task_runner.ts | 4 + .../detections_response/rules/index.ts | 1 + .../detections_response/rules/manual_run.ts | 42 +++ .../config/ess/config.base.ts | 1 + .../configs/serverless.config.ts | 3 + .../get_rule_execution_results.ts | 86 +++++- .../get_event_log_execute_complete_by_id.ts | 5 +- .../wait_for_event_log_execute_complete.ts | 5 +- .../rule_details/execution_log.cy.ts | 77 +++++ .../cypress/screens/rule_details.ts | 12 + .../cypress/tasks/api_calls/backfill.ts | 33 +++ .../cypress/tasks/rule_details.ts | 20 ++ 47 files changed, 1160 insertions(+), 107 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts create mode 100644 x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts index 36b00b1d4b6ef..82e8663bd6bf8 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.test.ts @@ -378,7 +378,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -472,7 +472,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -567,7 +567,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }); @@ -754,7 +754,7 @@ describe('AlertingEventLogger', () => { const event = createAlertRecord( backfillContext, ruleData, - [adHocRunSO, { id: 'bbb', type: 'alert', typeId: 'test' }], + [adHocRunSO, { id: 'bbb', type: 'alert', typeId: 'test', relation: 'primary' }], alert ); @@ -1130,7 +1130,7 @@ describe('AlertingEventLogger', () => { saved_objects: [ // @ts-ignore ...event.kibana?.saved_objects, - { id: 'bbb', type: 'alert', type_id: 'test' }, + { id: 'bbb', type: 'alert', type_id: 'test', rel: 'primary' }, ], }, }; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index 4b3a8b9d5201a..a7f9a2e2bebaa 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -255,6 +255,7 @@ export class AlertingEventLogger { typeId: type?.id, type: RULE_SAVED_OBJECT_TYPE, namespace: this.context?.namespace, + relation: SAVED_OBJECT_REL_PRIMARY, }); } } diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts index ddb132ebf64bb..216b32b013627 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/index.ts @@ -20,5 +20,6 @@ export * from './model/execution_result.gen'; export * from './model/execution_settings'; export * from './model/execution_status.gen'; export * from './model/execution_status'; +export * from './model/execution_run_type.gen'; export * from './model/execution_summary.gen'; export * from './model/log_level'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts index a1706fe0f4141..8a4c49d049d56 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.gen.ts @@ -41,6 +41,15 @@ export const RuleExecutionResult = z.object({ gap_duration_s: z.number().int(), security_status: z.string(), security_message: z.string(), + /** + * Backfill information for the rule execution result with source event date range + */ + backfill: z + .object({ + from: z.string().datetime(), + to: z.string().datetime(), + }) + .optional(), }); /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml index 3c8d91d3b9d7f..f8cf67bd423ea 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_result.schema.yaml @@ -52,6 +52,19 @@ components: type: string security_message: type: string + backfill: + type: object + description: Backfill information for the rule execution result with source event date range + properties: + from: + type: string + format: date-time + to: + type: string + format: date-time + required: + - from + - to required: - execution_uuid - timestamp diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts new file mode 100644 index 0000000000000..425d5c9e922af --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.gen.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Execution Run Type Schema + * version: not applicable + */ + +/** + * Type of rule execution run. + */ +export type RuleRunType = z.infer; +export const RuleRunType = z.enum(['backfill', 'standard']); +export type RuleRunTypeEnum = typeof RuleRunType.enum; +export const RuleRunTypeEnum = RuleRunType.enum; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml new file mode 100644 index 0000000000000..689e8393bcbe4 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/model/execution_run_type.schema.yaml @@ -0,0 +1,14 @@ +openapi: 3.0.0 +info: + title: Execution Run Type Schema + version: not applicable +paths: {} +components: + x-codegen-enabled: true + schemas: + RuleRunType: + type: string + description: Type of rule execution run. + enum: + - backfill + - standard \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts index 50cb863588a06..0c65eb39b88f4 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen.ts @@ -18,6 +18,7 @@ import { ArrayFromString } from '@kbn/zod-helpers'; */ import { RuleExecutionStatus } from '../../model/execution_status.gen'; +import { RuleRunType } from '../../model/execution_run_type.gen'; import { SortFieldOfRuleExecutionResult, RuleExecutionResult, @@ -44,6 +45,10 @@ export const GetRuleExecutionResultsRequestQuery = z.object({ * Comma-separated list of rule execution statuses to filter results by */ status_filters: ArrayFromString(RuleExecutionStatus).optional().default([]), + /** + * Comma-separated list of rule run types to filter results by + */ + run_type_filters: ArrayFromString(RuleRunType).optional().default([]), /** * Field to sort results by */ diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml index 8bba4b2811e31..42f8d54a2e616 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.schema.yaml @@ -48,6 +48,15 @@ paths: items: $ref: '../../model/execution_status.schema.yaml#/components/schemas/RuleExecutionStatus' default: [] + - name: run_type_filters + in: query + required: false + description: Comma-separated list of rule run types to filter results by + schema: + type: array + items: + $ref: '../../model/execution_run_type.schema.yaml#/components/schemas/RuleRunType' + default: [] - name: sort_field in: query required: false diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts index 742290de8bf43..4a3a7e74e9d35 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.test.ts @@ -255,6 +255,7 @@ describe('Request schema of Get rule execution results', () => { sort_order: 'desc', start: '2021-08-01T00:00:00.000Z', status_filters: [], + run_type_filters: [], }); }); }); diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 5be5462a805f9..c477a069c9c2e 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -434,6 +434,9 @@ export const NEW_FEATURES_TOUR_STORAGE_KEYS = { export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY = 'securitySolution.ruleDetails.ruleExecutionLog.showMetrics.v8.2'; +export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY = + 'securitySolution.ruleDetails.ruleExecutionLog.showSourceEventTimeRange.v8.15'; + // TODO: https://github.com/elastic/kibana/pull/142950 /** * Error codes that can be thrown during _bulk_action API dry_run call and be processed and displayed to end user diff --git a/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts b/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts new file mode 100644 index 0000000000000..9a2205b0b42c2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/rule_management/execution_log.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RuleExecutionStatus, RuleRunType } from '../../api/detection_engine/rule_monitoring'; +import { + RuleExecutionStatusEnum, + RuleRunTypeEnum, +} from '../../api/detection_engine/rule_monitoring'; + +export const RUN_TYPE_FILTERS: RuleRunType[] = [RuleRunTypeEnum.standard, RuleRunTypeEnum.backfill]; + +export const STATUS_FILTERS: RuleExecutionStatus[] = [ + RuleExecutionStatusEnum.succeeded, + RuleExecutionStatusEnum.failed, + RuleExecutionStatusEnum['partial failure'], +]; diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 01f03d4ac09ea..c3b6a09470a8a 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -286,6 +286,11 @@ export const allowedExperimentalValues = Object.freeze({ * Enables the new rule's bulk action to manage custom highlighted fields */ bulkCustomHighlightedFieldsEnabled: false, + + /** + * Enables the manual rule run + */ + manualRuleRunEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/public/common/translations.ts b/x-pack/plugins/security_solution/public/common/translations.ts index 126aa4eabe3b0..9720baa0d5aa0 100644 --- a/x-pack/plugins/security_solution/public/common/translations.ts +++ b/x-pack/plugins/security_solution/public/common/translations.ts @@ -113,3 +113,17 @@ export const getAgentTypeName = (agentType: ResponseActionAgentType) => { return agentType; } }; + +export const RULE_EXECUTION_TYPE_BACKFILL = i18n.translate( + 'xpack.securitySolution.detectionEngine.executionRunType.backfill', + { + defaultMessage: 'Manual', + } +); + +export const RULE_EXECUTION_TYPE_STANDARD = i18n.translate( + 'xpack.securitySolution.detectionEngine.executionRunType.standard', + { + defaultMessage: 'Scheduled', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx index a853e8f1422c7..45e84fcd24db9 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/__mocks__/rule_details_context.tsx @@ -31,6 +31,8 @@ export const useRuleDetailsContextMock = { sortField: 'timestamp', sortDirection: 'desc', }, + runTypeFilters: [], + showSourceEventTimeRange: true, }, actions: { setEnd: jest.fn(), @@ -45,6 +47,8 @@ export const useRuleDetailsContextMock = { setSortField: jest.fn(), setStart: jest.fn(), setStatusFilters: jest.fn(), + setRunTypeFilters: jest.fn(), + setShowSourceEventTimeRange: jest.fn(), }, }, }), diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap index 241f363d4885f..009e6dcc58ace 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/__snapshots__/execution_log_search_bar.test.tsx.snap @@ -10,17 +10,25 @@ exports[`ExecutionLogSearchBar snapshots renders correctly against snapshot 1`] - + + + + + `; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx index c8bacabbbbc4f..4428d6cca6de6 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_columns.tsx @@ -25,6 +25,10 @@ import type { import { getEmptyValue } from '../../../../../common/components/empty_value'; import { FormattedDate } from '../../../../../common/components/formatted_date'; +import { + RULE_EXECUTION_TYPE_BACKFILL, + RULE_EXECUTION_TYPE_STANDARD, +} from '../../../../../common/translations'; import { ExecutionStatusIndicator } from '../../../../rule_monitoring'; import { PopoverTooltip } from '../../../../rule_management_ui/components/rules_table/popover_tooltip'; import { TableHeaderTooltipCell } from '../../../../rule_management_ui/components/rules_table/table_header_tooltip_cell'; @@ -79,6 +83,19 @@ export const EXECUTION_LOG_COLUMNS: Array { + return ( + + {record.backfill ? RULE_EXECUTION_TYPE_BACKFILL : RULE_EXECUTION_TYPE_STANDARD} + + ); + }, + }, { field: 'timestamp', name: ( @@ -139,6 +156,34 @@ export const getMessageColumn = (width: string) => ({ width, }); +export const getSourceEventTimeRangeColumns = () => [ + { + name: ( + + ), + field: 'backfill', + render: (backfill: { to: string; from: string }) => { + return backfill ? ( +
+
+ +
+ {'-'} +
+ +
+
+ ) : ( + getEmptyValue() + ); + }, + width: '20%', + }, +]; + export const getExecutionLogMetricsColumns = ( docLinks: DocLinksStart ): Array> => [ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx index abda12afde763..584d3f93b474e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.test.tsx @@ -10,6 +10,10 @@ import React from 'react'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; import { noop } from 'lodash/fp'; +jest.mock('../../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + /** * NOTE: This component is currently not shown in the UI as custom search queries * are not yet fully supported by the Rule Execution Log aggregation API since @@ -27,6 +31,8 @@ describe('ExecutionLogSearchBar', () => { onlyShowFilters={true} selectedStatuses={[]} onStatusFilterChange={noop} + selectedRunTypes={[]} + onRunTypeFilterChange={noop} onSearch={noop} /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx index 3622182aa7fc3..db43b104ec713 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_search_bar.tsx @@ -9,10 +9,18 @@ import React, { useCallback } from 'react'; import { replace } from 'lodash'; import { EuiFieldSearch, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import type { RuleExecutionStatus } from '../../../../../../common/api/detection_engine/rule_monitoring'; -import { RuleExecutionStatusEnum } from '../../../../../../common/api/detection_engine/rule_monitoring'; -import { ExecutionStatusFilter } from '../../../../rule_monitoring'; +import type { + RuleExecutionStatus, + RuleRunType, +} from '../../../../../../common/api/detection_engine/rule_monitoring'; +import { + RUN_TYPE_FILTERS, + STATUS_FILTERS, +} from '../../../../../../common/detection_engine/rule_management/execution_log'; + +import { ExecutionStatusFilter, ExecutionRunTypeFilter } from '../../../../rule_monitoring'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; import * as i18n from './translations'; export const EXECUTION_LOG_SCHEMA_MAPPING = { @@ -36,18 +44,13 @@ export const replaceQueryTextAliases = (queryText: string): string => { ); }; -// This only includes statuses which are or can be final -const STATUS_FILTERS: RuleExecutionStatus[] = [ - RuleExecutionStatusEnum.succeeded, - RuleExecutionStatusEnum.failed, - RuleExecutionStatusEnum['partial failure'], -]; - interface ExecutionLogTableSearchProps { onlyShowFilters: true; selectedStatuses: RuleExecutionStatus[]; onStatusFilterChange: (selectedStatuses: RuleExecutionStatus[]) => void; onSearch: (queryText: string) => void; + selectedRunTypes: RuleRunType[]; + onRunTypeFilterChange: (selectedRunTypes: RuleRunType[]) => void; } /** @@ -58,13 +61,21 @@ interface ExecutionLogTableSearchProps { * Please see this comment for history/details: https://github.com/elastic/kibana/pull/127339/files#r825240516 */ export const ExecutionLogSearchBar = React.memo( - ({ onlyShowFilters, selectedStatuses, onStatusFilterChange, onSearch }) => { + ({ + onlyShowFilters, + selectedStatuses, + onStatusFilterChange, + onSearch, + selectedRunTypes, + onRunTypeFilterChange, + }) => { const handleSearch = useCallback( (queryText: string) => { onSearch(replaceQueryTextAliases(queryText)); }, [onSearch] ); + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); return ( @@ -81,11 +92,24 @@ export const ExecutionLogSearchBar = React.memo( )} - + + {isManualRuleRunEnabled && ( + + + + )} + + + + ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx index 7770548075511..26b3523c05472 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/execution_log_table.tsx @@ -33,7 +33,10 @@ import type { ThemeServiceStart } from '@kbn/core-theme-browser'; import { InputsModelId } from '../../../../../common/store/inputs/constants'; -import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../../common/constants'; +import { + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, +} from '../../../../../../common/constants'; import type { RuleExecutionResult, RuleExecutionStatus, @@ -72,8 +75,10 @@ import { getMessageColumn, getExecutionLogMetricsColumns, expanderColumn, + getSourceEventTimeRangeColumns, } from './execution_log_columns'; import { ExecutionLogSearchBar } from './execution_log_search_bar'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; const EXECUTION_UUID_FIELD_NAME = 'kibana.alert.rule.execution.uuid'; @@ -115,6 +120,7 @@ const ExecutionLogTableComponent: React.FC = ({ storage, timelines, } = useKibana().services; + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); const { [RuleDetailTabs.executionResults]: { @@ -122,7 +128,9 @@ const ExecutionLogTableComponent: React.FC = ({ superDatePicker: { recentlyUsedRanges, refreshInterval, isPaused, start, end }, queryText, statusFilters, + runTypeFilters, showMetricColumns, + showSourceEventTimeRange, pagination: { pageIndex, pageSize }, sort: { sortField, sortDirection }, }, @@ -139,6 +147,8 @@ const ExecutionLogTableComponent: React.FC = ({ setSortField, setStart, setStatusFilters, + setRunTypeFilters, + setShowSourceEventTimeRange, }, }, } = useRuleDetailsContext(); @@ -207,6 +217,7 @@ const ExecutionLogTableComponent: React.FC = ({ end, queryText, statusFilters, + runTypeFilters, page: pageIndex, perPage: pageSize, sortField, @@ -348,6 +359,17 @@ const ExecutionLogTableComponent: React.FC = ({ ] ); + const onShowSourceEventTimeRange = useCallback( + (showEventTimeRange: boolean) => { + storage.set( + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, + showEventTimeRange + ); + setShowSourceEventTimeRange(showEventTimeRange); + }, + [setShowSourceEventTimeRange, storage] + ); + const onShowMetricColumnsCallback = useCallback( (showMetrics: boolean) => { storage.set(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, showMetrics); @@ -431,12 +453,27 @@ const ExecutionLogTableComponent: React.FC = ({ }); const executionLogColumns = useMemo(() => { - const columns = [...EXECUTION_LOG_COLUMNS]; + const columns = [...EXECUTION_LOG_COLUMNS].filter((item) => { + if ('field' in item) { + return item.field === 'type' ? isManualRuleRunEnabled : true; + } + return true; + }); + let messageColumnWidth = 50; + + if (showSourceEventTimeRange && isManualRuleRunEnabled) { + columns.push(...getSourceEventTimeRangeColumns()); + messageColumnWidth = 30; + } if (showMetricColumns) { - columns.push(getMessageColumn('20%'), ...getExecutionLogMetricsColumns(docLinks)); + messageColumnWidth = 20; + columns.push( + getMessageColumn(`${messageColumnWidth}%`), + ...getExecutionLogMetricsColumns(docLinks) + ); } else { - columns.push(getMessageColumn('50%')); + columns.push(getMessageColumn(`${messageColumnWidth}%`)); } columns.push( @@ -448,10 +485,18 @@ const ExecutionLogTableComponent: React.FC = ({ ); return columns; - }, [actions, docLinks, showMetricColumns, rows.toggleRowExpanded, rows.isRowExpanded]); + }, [ + isManualRuleRunEnabled, + actions, + docLinks, + showMetricColumns, + showSourceEventTimeRange, + rows.toggleRowExpanded, + rows.isRowExpanded, + ]); return ( - + {/* Filter bar */} @@ -463,6 +508,8 @@ const ExecutionLogTableComponent: React.FC = ({ selectedStatuses={statusFilters} onStatusFilterChange={onStatusFilterChangeCallback} onSearch={onSearchCallback} + selectedRunTypes={runTypeFilters} + onRunTypeFilterChange={setRunTypeFilters} /> @@ -516,6 +563,14 @@ const ExecutionLogTableComponent: React.FC = ({ updatedAt: dataUpdatedAt, })} + {isManualRuleRunEnabled && ( + onShowSourceEventTimeRange(e.target.checked)} + /> + )} = ({ onChange={onTableChangeCallback} itemId={getItemId} itemIdToExpandedRowMap={rows.itemIdToExpandedRowMap} + data-test-subj="executionsTable" /> ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts index 0b4e91c5934af..f84e0b6b859c2 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/execution_log_table/translations.ts @@ -55,6 +55,13 @@ export const RULE_EXECUTION_LOG_SHOW_METRIC_COLUMNS_SWITCH = i18n.translate( } ); +export const RULE_EXECUTION_LOG_SHOW_SOURCE_EVENT_TIME_RANGE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.showSourceEventTimeRangeSwitchTitle', + { + defaultMessage: 'Show source event time range', + } +); + export const COLUMN_STATUS = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.statusColumn', { @@ -69,6 +76,27 @@ export const COLUMN_STATUS_TOOLTIP = i18n.translate( } ); +export const COLUMN_TYPE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.type', + { + defaultMessage: 'Type', + } +); + +export const COLUMN_SOURCE_EVENT_TIME_RANGE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.sourceEventTimeRange', + { + defaultMessage: 'Source event time range', + } +); + +export const COLUMN_SOURCE_EVENT_TIME_RANGE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.sourceEventTimeRangeTooltip', + { + defaultMessage: "Only for manual rule runs. Don't include additional lookback time.", + } +); + export const COLUMN_TIMESTAMP = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.ruleExecutionLog.timestampColumn', { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx index e08e32662cca6..7134fb981c790 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/rule_details_context.tsx @@ -8,10 +8,14 @@ import type { SortOrder } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { DurationRange } from '@elastic/eui/src/components/date_picker/types'; import React, { createContext, useContext, useMemo, useState } from 'react'; -import { RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY } from '../../../../../common/constants'; +import { + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY, + RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY, +} from '../../../../../common/constants'; import type { RuleExecutionResult, RuleExecutionStatus, + RuleRunType, } from '../../../../../common/api/detection_engine/rule_monitoring'; import { invariant } from '../../../../../common/utils/invariant'; import { useKibana } from '../../../../common/lib/kibana'; @@ -55,6 +59,11 @@ export interface ExecutionLogTableState { * Whether or not to show additional metric columnbs */ showMetricColumns: boolean; + /** + * Whether or not to show source event time range + */ + + showSourceEventTimeRange: boolean; /** * Currently selected page and number of rows per page */ @@ -66,6 +75,7 @@ export interface ExecutionLogTableState { sortField: keyof RuleExecutionResult; sortDirection: SortOrder; }; + runTypeFilters: RuleRunType[]; } // @ts-expect-error unused constant @@ -79,7 +89,9 @@ const DEFAULT_STATE: ExecutionLogTableState = { }, queryText: '', statusFilters: [], + runTypeFilters: [], showMetricColumns: false, + showSourceEventTimeRange: false, pagination: { pageIndex: 1, pageSize: 5, @@ -103,6 +115,8 @@ export interface ExecutionLogTableActions { setPageSize: React.Dispatch>; setSortField: React.Dispatch>; setSortDirection: React.Dispatch>; + setShowSourceEventTimeRange: React.Dispatch>; + setRunTypeFilters: React.Dispatch>; } export interface RuleDetailsContextType { @@ -133,9 +147,13 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi // Searchbar/Filter/Settings state const [queryText, setQueryText] = useState(''); const [statusFilters, setStatusFilters] = useState([]); + const [runTypeFilters, setRunTypeFilters] = useState([]); const [showMetricColumns, setShowMetricColumns] = useState( storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY) ?? false ); + const [showSourceEventTimeRange, setShowSourceEventTimeRange] = useState( + storage.get(RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_SOURCE_EVENT_TIME_RANGE_STORAGE_KEY) ?? false + ); // Pagination state const [pageIndex, setPageIndex] = useState(1); const [pageSize, setPageSize] = useState(5); @@ -156,7 +174,9 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi }, queryText, statusFilters, + runTypeFilters, showMetricColumns, + showSourceEventTimeRange, pagination: { pageIndex, pageSize, @@ -179,6 +199,8 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi setSortField, setStart, setStatusFilters, + setRunTypeFilters, + setShowSourceEventTimeRange, }, }, }), @@ -195,6 +217,8 @@ export const RuleDetailsContextProvider = ({ children }: RuleDetailsContextProvi sortField, start, statusFilters, + runTypeFilters, + showSourceEventTimeRange, ] ); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts index bd1bf80a0f4c3..9290e97dcfcd7 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client.ts @@ -87,6 +87,7 @@ export const api: IRuleMonitoringApiClient = { sortField, sortOrder, signal, + runTypeFilters, } = args; const url = getRuleExecutionResultsUrl(ruleId); @@ -105,6 +106,7 @@ export const api: IRuleMonitoringApiClient = { sort_order: sortOrder, page, per_page: perPage, + run_type_filters: runTypeFilters?.sort()?.join(','), }, signal, }); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts index a51059a16ef42..06170430a455e 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/api/api_client_interface.ts @@ -13,6 +13,7 @@ import type { RuleExecutionEventType, RuleExecutionResult, RuleExecutionStatus, + RuleRunType, } from '../../../../common/api/detection_engine/rule_monitoring'; export interface IRuleMonitoringApiClient { @@ -139,4 +140,9 @@ export interface FetchRuleExecutionResultsArgs extends RuleMonitoringApiCallArgs * Number of results to fetch per page. */ perPage?: number; + + /** + * Array of `runTypeFilters` (e.g. `manual,scheduled`) + */ + runTypeFilters?: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx new file mode 100644 index 0000000000000..773e64e71ffc9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/index.tsx @@ -0,0 +1,52 @@ +/* + * 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, { useCallback } from 'react'; +import { MultiselectFilter } from '../multiselect_filter'; +import type { RuleRunType } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import { RuleRunTypeEnum } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import * as i18n from './translations'; +import { + RULE_EXECUTION_TYPE_BACKFILL, + RULE_EXECUTION_TYPE_STANDARD, +} from '../../../../../../common/translations'; + +interface ExecutionRunTypeFilterProps { + items: RuleRunType[]; + selectedItems: RuleRunType[]; + onChange: (selectedItems: RuleRunType[]) => void; +} + +const ExecutionRunTypeFilterComponent: React.FC = ({ + items, + selectedItems, + onChange, +}) => { + const renderItem = useCallback((item: RuleRunType) => { + if (item === RuleRunTypeEnum.backfill) { + return RULE_EXECUTION_TYPE_BACKFILL; + } else if (item === RuleRunTypeEnum.standard) { + return RULE_EXECUTION_TYPE_STANDARD; + } else { + return '-'; + } + }, []); + + return ( + + dataTestSubj="ExecutionRunTypeFilter" + title={i18n.FILTER_TITLE} + items={items} + selectedItems={selectedItems} + onSelectionChange={onChange} + renderItem={renderItem} + /> + ); +}; + +export const ExecutionRunTypeFilter = React.memo(ExecutionRunTypeFilterComponent); +ExecutionRunTypeFilter.displayName = 'ExecutionRunTypeFilter'; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts new file mode 100644 index 0000000000000..18ace6b7b1ca0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/basic/filters/execution_run_type_filter/translations.ts @@ -0,0 +1,15 @@ +/* + * 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'; + +export const FILTER_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleMonitoring.executionRunTypeFilter.filterTitle', + { + defaultMessage: 'Run type', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx index 65d7ea7c3cda7..d5d2e2409e2a3 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.test.tsx @@ -12,6 +12,10 @@ import { useToasts } from '../../../../common/lib/kibana'; import { api } from '../../api'; import { createReactQueryWrapper } from '../../../../common/mock'; +jest.mock('../../../../common/hooks/use_experimental_features', () => ({ + useIsExperimentalFeatureEnabled: jest.fn(), +})); + jest.mock('../../../../common/lib/kibana'); jest.mock('../../api'); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx index ac18f11c75907..8660139676351 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/components/execution_results_table/use_execution_results.tsx @@ -8,21 +8,30 @@ import { useQuery } from '@tanstack/react-query'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; +import { RuleRunTypeEnum } from '../../../../../common/api/detection_engine/rule_monitoring'; import type { GetRuleExecutionResultsResponse } from '../../../../../common/api/detection_engine/rule_monitoring'; import type { FetchRuleExecutionResultsArgs } from '../../api'; import { api } from '../../api'; - +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import * as i18n from './translations'; export type UseExecutionResultsArgs = Omit; export const useExecutionResults = (args: UseExecutionResultsArgs) => { const { addError } = useAppToasts(); + const isManualRuleRunEnabled = useIsExperimentalFeatureEnabled('manualRuleRunEnabled'); return useQuery( ['detectionEngine', 'ruleMonitoring', 'executionResults', args], ({ signal }) => { - return api.fetchRuleExecutionResults({ ...args, signal }); + let runTypeFilters = args.runTypeFilters; + + // if manual rule run is disabled, only show standard runs + if (!isManualRuleRunEnabled) { + runTypeFilters = [RuleRunTypeEnum.standard]; + } + + return api.fetchRuleExecutionResults({ ...args, runTypeFilters, signal }); }, { keepPreviousData: true, diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts index b186a8d7fd837..9eb42fb3d6b6c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_monitoring/index.ts @@ -8,6 +8,7 @@ export * from './api'; export * from './components/basic/filters/execution_status_filter'; +export * from './components/basic/filters/execution_run_type_filter'; export * from './components/basic/indicators/execution_status_indicator'; export * from './components/execution_events_table/execution_events_table'; export * from './components/execution_results_table/use_execution_results'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts index e1981cf9a3937..6c71652d27e34 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/api/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.ts @@ -56,6 +56,7 @@ export const getRuleExecutionResultsRoute = (router: SecuritySolutionPluginRoute per_page: perPage, sort_field: sortField, sort_order: sortOrder, + run_type_filters: runTypeFilters, } = request.query; const siemResponse = buildSiemResponse(response); @@ -73,6 +74,7 @@ export const getRuleExecutionResultsRoute = (router: SecuritySolutionPluginRoute perPage, sortField, sortOrder, + runTypeFilters, }); return response.ok({ body: executionResultsResponse }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts index 6f3c867ee3ed4..2110294d6eb0e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/client_for_routes/client_interface.ts @@ -12,6 +12,7 @@ import type { RuleExecutionEventType, RuleExecutionStatus, SortFieldOfRuleExecutionResult, + RuleRunType, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; import type { RuleObjectId } from '../../../../../../../common/api/detection_engine/model/rule_schema'; import type { SortOrder } from '../../../../../../../common/api/detection_engine'; @@ -91,4 +92,6 @@ export interface GetExecutionResultsArgs { /** Number of results to fetch per page. */ perPage: number; + + runTypeFilters: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts index 79ce2503cbe9b..cfa8a4b36a375 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.test.ts @@ -33,6 +33,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid maxExecutions requested \\"1001\\" - must be less than ${MAX_EXECUTION_EVENTS_DISPLAYED}"` @@ -46,6 +47,7 @@ describe('getExecutionEventAggregation', () => { page: 0, perPage: 10, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot(`"Invalid page field \\"0\\" - must be greater than 0"`); }); @@ -57,6 +59,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 0, sort: [{ timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid perPage field \\"0\\" - must be greater than 0"` @@ -70,6 +73,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ notsortable: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` @@ -83,6 +87,7 @@ describe('getExecutionEventAggregation', () => { page: 1, perPage: 10, sort: [{ notsortable: { order: 'asc' } }, { timestamp: { order: 'asc' } }], + runTypeFilters: [], }); }).toThrowErrorMatchingInlineSnapshot( `"Invalid sort field \\"notsortable\\" - must be one of [timestamp,duration_ms,indexing_duration_ms,search_duration_ms,gap_duration_s,schedule_delay_ms,num_triggered_actions]"` @@ -96,6 +101,7 @@ describe('getExecutionEventAggregation', () => { page: 2, perPage: 10, sort: [{ timestamp: { order: 'asc' } }, { duration_ms: { order: 'desc' } }], + runTypeFilters: [], }) ).toEqual({ totalExecutions: { @@ -139,15 +145,18 @@ describe('getExecutionEventAggregation', () => { actionExecution: { filter: { bool: { + minimum_should_match: 1, must: [ { match: { - 'event.action': 'execute', + 'event.provider': 'actions', }, }, + ], + should: [ { match: { - 'event.provider': 'actions', + 'event.action': 'execute', }, }, ], @@ -165,7 +174,15 @@ describe('getExecutionEventAggregation', () => { ruleExecution: { filter: { bool: { + minimum_should_match: 1, must: [ + { + match: { + 'event.provider': 'alerting', + }, + }, + ], + should: [ { match: { 'event.action': 'execute', @@ -173,7 +190,7 @@ describe('getExecutionEventAggregation', () => { }, { match: { - 'event.provider': 'alerting', + 'event.action': 'execute-backfill', }, }, ], @@ -213,6 +230,14 @@ describe('getExecutionEventAggregation', () => { }, }, }, + backfill: { + top_hits: { + size: 1, + _source: { + includes: ['kibana.alert.rule.execution.backfill'], + }, + }, + }, }, }, securityMetrics: { @@ -221,15 +246,18 @@ describe('getExecutionEventAggregation', () => { must: [ { match: { - 'event.action': 'execution-metrics', + 'event.provider': 'securitySolution.ruleExecution', }, }, + ], + should: [ { match: { - 'event.provider': 'securitySolution.ruleExecution', + 'event.action': 'execution-metrics', }, }, ], + minimum_should_match: 1, }, }, aggs: { @@ -257,15 +285,18 @@ describe('getExecutionEventAggregation', () => { must: [ { match: { - 'event.action': 'status-change', + 'event.provider': 'securitySolution.ruleExecution', }, }, + ], + should: [ { match: { - 'event.provider': 'securitySolution.ruleExecution', + 'event.action': 'status-change', }, }, ], + minimum_should_match: 1, }, }, aggs: { @@ -300,15 +331,18 @@ describe('getExecutionEventAggregation', () => { timeoutMessage: { filter: { bool: { + minimum_should_match: 1, must: [ { match: { - 'event.action': 'execute-timeout', + 'event.provider': 'alerting', }, }, + ], + should: [ { match: { - 'event.provider': 'alerting', + 'event.action': 'execute-timeout', }, }, ], @@ -325,10 +359,9 @@ describe('getProviderAndActionFilter', () => { test('should correctly format array of sort combinations for bucket sorting', () => { expect(getProviderAndActionFilter('securitySolution.ruleExecution', 'status-change')).toEqual({ bool: { - must: [ - { match: { 'event.action': 'status-change' } }, - { match: { 'event.provider': 'securitySolution.ruleExecution' } }, - ], + minimum_should_match: 1, + must: [{ match: { 'event.provider': 'securitySolution.ruleExecution' } }], + should: [{ match: { 'event.action': 'status-change' } }], }, }); }); @@ -363,6 +396,7 @@ describe('formatExecutionEventResponse', () => { events: [], }); }); + test('should format results correctly', () => { const results = { aggregations: { @@ -1321,6 +1355,212 @@ describe('formatExecutionEventResponse', () => { ], }); }); + + test('should format results correctly when backfull occur', () => { + const results = { + aggregations: { + executionUuid: { + meta: {}, + doc_count_error_upper_bound: -1, + sum_other_doc_count: 1184, + buckets: [ + { + key: '02b7c7a4-ae1a-4da5-b134-c2fb96eb0e04', + doc_count: 5, + timeoutMessage: { + meta: {}, + doc_count: 0, + }, + securityMetrics: { + meta: {}, + doc_count: 1, + searchDuration: { + value: 9.0, + }, + indexDuration: { + value: 0.0, + }, + gapDuration: { + value: 0.0, + }, + }, + ruleExecution: { + meta: {}, + doc_count: 1, + numTriggeredActions: { + value: 0.0, + }, + scheduleDelay: { + value: 9.96e8, + }, + outcomeAndMessage: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'pK84iX8Brb7RSEAg3a-L', + _score: 1.0, + _source: { + event: { + outcome: 'success', + }, + message: + "rule executed: siem.queryRule:7457b121-a3a8-11ec-a0f0-cbd1c2ae6ee8: 'Endpoint Security'", + }, + }, + ], + }, + }, + backfill: { + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 1.0, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'J4T5fI8BE4fKEPKOLKON', + _score: 0.003465116, + _source: { + kibana: { + alert: { + rule: { + execution: { + backfill: { + id: '341536e6-5ac9-4d0e-a25c-dc1c70cda7d5', + start: '2024-04-01T17:50:00.000Z', + interval: '1m', + }, + }, + }, + }, + }, + }, + }, + ], + }, + }, + esSearchDuration: { + value: 5.0, + }, + executionDuration: { + value: 1.922e9, + }, + executeStartTime: { + value: 1.647274677664e12, + value_as_string: '2022-03-14T16:17:57.664Z', + }, + }, + actionExecution: { + meta: {}, + doc_count: 0, + actionOutcomes: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, + securityStatus: { + meta: {}, + doc_count: 2, + message: { + hits: { + total: { + value: 2, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'o684iX8Brb7RSEAg2a-j', + _score: null, + _source: { + message: 'succeeded', + }, + sort: [1647274678629], + }, + ], + }, + }, + status: { + hits: { + total: { + value: 2, + relation: 'eq', + }, + max_score: null, + hits: [ + { + _index: '.kibana-event-log-8.2.0-000001', + _id: 'o684iX8Brb7RSEAg2a-j', + _score: null, + _source: { + kibana: { + alert: { + rule: { + execution: { + status: 'succeeded', + }, + }, + }, + }, + }, + sort: [1647274678629], + }, + ], + }, + }, + }, + }, + ], + }, + totalExecutions: { + value: 768, + }, + }, + }; + + expect(formatExecutionEventResponse(results)).toEqual({ + total: 768, + events: [ + { + execution_uuid: '02b7c7a4-ae1a-4da5-b134-c2fb96eb0e04', + timestamp: '2022-03-14T16:17:57.664Z', + duration_ms: 1922, + status: 'success', + message: + "rule executed: siem.queryRule:7457b121-a3a8-11ec-a0f0-cbd1c2ae6ee8: 'Endpoint Security'", + num_active_alerts: 0, + num_new_alerts: 0, + num_recovered_alerts: 0, + num_triggered_actions: 0, + num_succeeded_actions: 0, + num_errored_actions: 0, + total_search_duration_ms: 0, + es_search_duration_ms: 5, + schedule_delay_ms: 996, + timed_out: false, + indexing_duration_ms: 0, + search_duration_ms: 9, + gap_duration_s: 0, + security_status: 'succeeded', + security_message: 'succeeded', + backfill: { + to: '2024-04-01T17:50:00.000Z', + from: '2024-04-01T17:49:00.000Z', + }, + }, + ], + }); + }); }); describe('mapRuleStatusToPlatformStatus', () => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts index e4e394fd16149..a744732ddc32d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/index.ts @@ -10,7 +10,8 @@ import type { AggregateEventsBySavedObjectResult } from '@kbn/event-log-plugin/s import { BadRequestError } from '@kbn/securitysolution-es-utils'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; import { flatMap, get } from 'lodash'; - +import { parseDuration } from '@kbn/alerting-plugin/server'; +import moment from 'moment'; import type { GetRuleExecutionResultsResponse, RuleExecutionResult, @@ -48,6 +49,7 @@ const GAP_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.execution_gap_du const INDEXING_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.total_indexing_duration_ms'; const SEARCH_DURATION_FIELD = 'kibana.alert.rule.execution.metrics.total_search_duration_ms'; const STATUS_FIELD = 'kibana.alert.rule.execution.status'; +const BACKFILL_FIELD = 'kibana.alert.rule.execution.backfill'; const ONE_MILLISECOND_AS_NANOSECONDS = 1_000_000; @@ -143,7 +145,7 @@ export const getExecutionEventAggregation = ({ }, // Filter by alerting execute doc to retrieve platform metrics ruleExecution: { - filter: getProviderAndActionFilter('alerting', 'execute'), + filter: getProviderAndActionFilter('alerting', ['execute', 'execute-backfill']), aggs: { executeStartTime: { min: { @@ -170,6 +172,14 @@ export const getExecutionEventAggregation = ({ field: DURATION_FIELD, }, }, + backfill: { + top_hits: { + size: 1, + _source: { + includes: [BACKFILL_FIELD], + }, + }, + }, outcomeAndMessage: { top_hits: { size: 1, @@ -248,25 +258,46 @@ export const getExecutionEventAggregation = ({ * @param provider provider to match * @param action action to match */ -export const getProviderAndActionFilter = (provider: string, action: string) => { +export const getProviderAndActionFilter = (provider: string, action: string | string[]) => { + const actions = Array.isArray(action) ? action : [action]; return { bool: { must: [ - { - match: { - [ACTION_FIELD]: action, - }, - }, { match: { [PROVIDER_FIELD]: provider, }, }, ], + should: actions.map((a) => ({ + match: { + [ACTION_FIELD]: a, + }, + })), + minimum_should_match: 1, }, }; }; +const getBackfill = (bucket: ExecutionUuidAggBucket) => { + const backfill = + bucket?.ruleExecution?.backfill?.hits?.hits[0]?._source?.kibana?.alert?.rule?.execution + ?.backfill; + + if (backfill) { + backfill.from = moment(backfill.start) + .subtract(parseDuration(backfill.interval), 'ms') + .toISOString(); + + return { + from: moment(backfill.start).subtract(parseDuration(backfill.interval), 'ms').toISOString(), + to: backfill.start, + }; + } + + return backfill; +}; + /** * Formats aggregate execution event from bucket response * @param bucket @@ -281,6 +312,7 @@ export const formatAggExecutionEventFromBucket = ( const actionOutcomes = bucket?.actionExecution?.actionOutcomes?.buckets ?? []; const actionExecutionSuccess = actionOutcomes.find((b) => b?.key === 'success')?.doc_count ?? 0; const actionExecutionError = actionOutcomes.find((b) => b?.key === 'failure')?.doc_count ?? 0; + const backfill = getBackfill(bucket); return { execution_uuid: bucket?.key ?? '', @@ -313,6 +345,7 @@ export const formatAggExecutionEventFromBucket = ( security_message: bucket?.securityStatus?.message?.hits?.hits[0]?._source?.message ?? bucket?.ruleExecution?.outcomeAndMessage?.hits?.hits[0]?._source?.error?.message, + backfill, }; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts index 429da6eeac8bc..c09a8c4edc57b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/aggregations/execution_results/types.ts @@ -6,6 +6,7 @@ */ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { RuleRunType } from '../../../../../../../../../common/api/detection_engine/rule_monitoring'; type AlertCounts = estypes.AggregationsMultiBucketAggregateBase & { buckets: { @@ -32,6 +33,7 @@ export type ExecutionUuidAggBucket = estypes.AggregationsStringTermsBucketKeys & totalSearchDuration: estypes.AggregationsMaxAggregate; numTriggeredActions: estypes.AggregationsMaxAggregate; outcomeAndMessage: estypes.AggregationsTopHitsAggregate; + backfill: estypes.AggregationsTopHitsAggregate; }; alertCounts: AlertCounts; actionExecution: { @@ -58,4 +60,5 @@ export interface ExecutionEventAggregationOptions { page: number; perPage: number; sort: estypes.Sort; + runTypeFilters: RuleRunType[]; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts index 669f3d7e5ee04..768e32d277b94 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/rule_execution_log/event_log/event_log_reader.ts @@ -8,18 +8,23 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IEventLogClient, IValidatedEvent } from '@kbn/event-log-plugin/server'; import { MAX_EXECUTION_EVENTS_DISPLAYED } from '@kbn/securitysolution-rules'; - import type { GetRuleExecutionEventsResponse, GetRuleExecutionResultsResponse, RuleExecutionEvent, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; import { + RuleRunTypeEnum, LogLevel, LogLevelEnum, RuleExecutionEventType, RuleExecutionEventTypeEnum, } from '../../../../../../../common/api/detection_engine/rule_monitoring'; +import { + RUN_TYPE_FILTERS, + STATUS_FILTERS, +} from '../../../../../../../common/detection_engine/rule_management/execution_log'; + import { prepareKQLStringParam } from '../../../../../../../common/utils/kql'; import { assertUnreachable } from '../../../../../../../common/utility_types'; @@ -100,75 +105,104 @@ export const createEventLogReader = (eventLog: IEventLogClient): IEventLogReader async getExecutionResults( args: GetExecutionResultsArgs ): Promise { - const { ruleId, start, end, statusFilters, page, perPage, sortField, sortOrder } = args; + const { + ruleId, + start, + end, + statusFilters, + page, + perPage, + sortField, + sortOrder, + runTypeFilters, + } = args; const soType = RULE_SAVED_OBJECT_TYPE; const soIds = [ruleId]; + let totalExecutions: number | undefined; + let executionIdsFilter: string = ''; + + // Similar workaround to the above for status filters + // First fetch execution uuid's by run type filter if provided + // Then use those ID's to filter by status if provided + const MAX_RUN_TYPE_FILTERS = RUN_TYPE_FILTERS.length; + const someRunTypeFiltersSelected = + runTypeFilters.length > 0 && runTypeFilters.length < MAX_RUN_TYPE_FILTERS; + if (someRunTypeFiltersSelected) { + const ruleRunEventActions = runTypeFilters.map((runType) => { + return { + [RuleRunTypeEnum.standard]: 'execute', + [RuleRunTypeEnum.backfill]: 'execute-backfill', + }[runType]; + }); + + const { executionIds, total } = await findRuleExecutionIds({ + eventLog, + soType, + soIds, + start, + end, + filter: `event.provider:alerting AND (${ruleRunEventActions + .map((runType) => `event.action:${runType}`) + .join(' OR ')}) `, + }); + + if (executionIds.length === 0) { + return { + total: 0, + events: [], + }; + } else { + totalExecutions = total; + executionIdsFilter = `${f.RULE_EXECUTION_UUID}:(${executionIds.join(' OR ')})`; + } + } + // Current workaround to support root level filters without missing fields in the aggregate event // or including events from statuses that aren't selected // TODO: See: https://github.com/elastic/kibana/pull/127339/files#r825240516 // First fetch execution uuid's by status filter if provided - let statusIds: string[] = []; - let totalExecutions: number | undefined; // If 0 or 3 statuses are selected we can search for all statuses and don't need this pre-filter by ID - if (statusFilters.length > 0 && statusFilters.length < 3) { + const MAX_STATUSES_FILTERS = STATUS_FILTERS.length; + const someStatusFiltersSelected = + statusFilters.length > 0 && statusFilters.length < MAX_STATUSES_FILTERS; + if (someStatusFiltersSelected) { const outcomes = mapRuleExecutionStatusToPlatformStatus(statusFilters); const outcomeFilter = outcomes.length ? `OR event.outcome:(${outcomes.join(' OR ')})` : ''; - const statusResults = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { + const { executionIds, total } = await findRuleExecutionIds({ + eventLog, + soType, + soIds, start, end, - // Also query for `event.outcome` to catch executions that only contain platform events - filter: `${f.RULE_EXECUTION_STATUS}:(${statusFilters.join(' OR ')}) ${outcomeFilter}`, - aggs: { - totalExecutions: { - cardinality: { - field: f.RULE_EXECUTION_UUID, - }, - }, - filteredExecutionUUIDs: { - terms: { - field: f.RULE_EXECUTION_UUID, - order: { executeStartTime: 'desc' }, - size: MAX_EXECUTION_EVENTS_DISPLAYED, - }, - aggs: { - executeStartTime: { - min: { - field: f.TIMESTAMP, - }, - }, - }, - }, - }, + filter: `(${f.RULE_EXECUTION_STATUS}:(${statusFilters.join(' OR ')}) ${outcomeFilter}) ${ + executionIdsFilter ? `AND ${executionIdsFilter}` : '' + }`, }); - const filteredExecutionUUIDs = statusResults.aggregations - ?.filteredExecutionUUIDs as ExecutionUuidAggResult; - statusIds = filteredExecutionUUIDs?.buckets?.map((b) => b.key) ?? []; - totalExecutions = ( - statusResults.aggregations?.totalExecutions as estypes.AggregationsCardinalityAggregate - ).value; + // Early return if no results based on status filter - if (statusIds.length === 0) { + if (executionIds.length === 0) { return { total: 0, events: [], }; + } else { + executionIdsFilter = `${f.RULE_EXECUTION_UUID}:(${executionIds.join(' OR ')})`; + totalExecutions = total; } } // Now query for aggregate events, and pass any ID's as filters as determined from the above status/queryText results - const idsFilter = statusIds.length - ? `${f.RULE_EXECUTION_UUID}:(${statusIds.join(' OR ')})` - : ''; const results = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { start, end, - filter: idsFilter, + filter: executionIdsFilter, aggs: getExecutionEventAggregation({ maxExecutions: MAX_EXECUTION_EVENTS_DISPLAYED, page, perPage, sort: [{ [sortField]: { order: sortOrder } }], + runTypeFilters, }), }); @@ -305,3 +339,58 @@ const buildEventLogKqlFilter = ({ return kqlAnd(filters); }; + +const findRuleExecutionIds = async ({ + eventLog, + soType, + soIds, + start, + end, + filter, +}: { + eventLog: IEventLogClient; + soType: string; + soIds: string[]; + start: string; + end: string; + filter: string; +}) => { + const runTypesResponse = await eventLog.aggregateEventsBySavedObjectIds(soType, soIds, { + start, + end, + filter, + aggs: { + totalExecutions: { + cardinality: { + field: f.RULE_EXECUTION_UUID, + }, + }, + filteredExecutionUUIDs: { + terms: { + field: f.RULE_EXECUTION_UUID, + order: { executeStartTime: 'desc' }, + size: MAX_EXECUTION_EVENTS_DISPLAYED, + }, + aggs: { + executeStartTime: { + min: { + field: f.TIMESTAMP, + }, + }, + }, + }, + }, + }); + + const total = + (runTypesResponse.aggregations?.totalExecutions as estypes.AggregationsCardinalityAggregate) + ?.value ?? 0; + const filteredExecutionUUIDs = runTypesResponse.aggregations + ?.filteredExecutionUUIDs as ExecutionUuidAggResult; + const executionIds = filteredExecutionUUIDs?.buckets?.map((b) => b.key) ?? []; + + return { + executionIds, + total, + }; +}; diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts index aa81a4713a8af..f513866cd5ee5 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group1/tests/alerting/backfill/task_runner.ts @@ -235,6 +235,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'siem.queryRule', @@ -467,6 +468,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', @@ -499,6 +501,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', @@ -604,6 +607,7 @@ export default function createBackfillTaskRunnerTests({ getService }: FtrProvide namespace: 'space1', }, { + rel: 'primary', type: RULE_SAVED_OBJECT_TYPE, id: ruleId, type_id: 'test.patternFiringAutoRecoverFalse', diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts index 41559d7c01c05..0f757b0a8d6a7 100644 --- a/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/index.ts @@ -10,3 +10,4 @@ export * from './delete_all_rules'; export * from './delete_rule'; export * from './get_rule_for_alert_testing'; export * from './wait_for_rule_status'; +export * from './manual_run'; diff --git a/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts b/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts new file mode 100644 index 0000000000000..8e1f47440009b --- /dev/null +++ b/x-pack/test/common/utils/security_solution/detections_response/rules/manual_run.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type SuperTest from 'supertest'; +import { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; +import { ScheduleBackfillResponse } from '@kbn/alerting-plugin/common/routes/backfill/apis/schedule'; +import { routeWithNamespace } from '../route_with_namespace'; + +const BACKFILL_RULE_URL = `${INTERNAL_BASE_ALERTING_API_PATH}/rules/backfill`; +const BACKFILL_RULE_URL_SCHEDULE = `${BACKFILL_RULE_URL}/_schedule`; + +export const manualRuleRun = async ({ + supertest, + ruleId, + start, + end, + namespace, +}: { + ruleId: string; + start: string; + end: string; + namespace?: string; + supertest: SuperTest.Agent; +}): Promise => { + const route = routeWithNamespace(BACKFILL_RULE_URL_SCHEDULE, namespace); + const response = await supertest + .post(route) + .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'Kibana') + .send([ + { + rule_id: ruleId, + start, + end, + }, + ]); + + return response; +}; diff --git a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts index d134546c6f633..0fbe6b12a7ade 100644 --- a/x-pack/test/security_solution_api_integration/config/ess/config.base.ts +++ b/x-pack/test/security_solution_api_integration/config/ess/config.base.ts @@ -84,6 +84,7 @@ export function createTestConfig(options: CreateTestConfigOptions, testFiles?: s 'riskScoringPersistence', 'riskScoringRoutesEnabled', 'bulkCustomHighlightedFieldsEnabled', + 'manualRuleRunEnabled', ])}`, '--xpack.task_manager.poll_interval=1000', `--xpack.actions.preconfigured=${JSON.stringify(PRECONFIGURED_ACTION_CONNECTORS)}`, diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts index ca9396db04661..52a1074c87904 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/configs/serverless.config.ts @@ -12,4 +12,7 @@ export default createTestConfig({ reportName: 'Rules Management - Rule Management Integration Tests - Serverless Env - Complete Tier', }, + kbnTestServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['manualRuleRunEnabled'])}`, + ], }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts index cd8c2a11f50f4..45280cd0ec51c 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_management/trial_license_complete_tier/get_rule_execution_results.ts @@ -29,6 +29,7 @@ import { getRuleForAlertTesting, waitForRulePartialFailure, waitForRuleSuccess, + manualRuleRun, } from '../../../../../../common/utils/security_solution'; import { failedGapExecution, @@ -51,7 +52,7 @@ export default ({ getService }: FtrProviderContext) => { // FLAKY: https://github.com/elastic/kibana/issues/177223 // Failing: See https://github.com/elastic/kibana/issues/177223 - describe.skip('@ess @serverless Get Rule Execution Results', () => { + describe('@ess @serverless Get Rule Execution Results', () => { before(async () => { await esArchiver.load(auditbeatPath); await esArchiver.load('x-pack/test/functional/es_archives/security_solution/alias'); @@ -261,5 +262,88 @@ export default ({ getService }: FtrProviderContext) => { expect(response.body.total).to.eql(1002); expect(response.body.events[0].duration_ms).to.eql(3); }); + + it('should return execution events with backfill information', async () => { + const rule = { + ...getRuleForAlertTesting(['auditbeat-*']), + query: 'process.executable: "/usr/bin/sudo"', + }; + const { id } = await createRule(supertest, log, rule); + const fromManualRuleRun = dateMath.parse('now-1m')?.utc().toISOString() ?? ''; + const toManualRuleRun = dateMath.parse('now', { roundUp: true })?.utc().toISOString() ?? ''; + await manualRuleRun({ + ruleId: id, + supertest, + start: fromManualRuleRun, + end: toManualRuleRun, + }); + + await waitForRuleSuccess({ supertest, log, id }); + await waitForEventLogExecuteComplete(es, log, id, 1, 'execute-backfill'); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const response = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: ['backfill'] }); + + expect(response.status).to.eql(200); + expect(response.body.total).to.eql(1); + expect(response.body.events[0].duration_ms).to.greaterThan(0); + expect(response.body.events[0].search_duration_ms).to.greaterThan(0); + expect(response.body.events[0].schedule_delay_ms).to.greaterThan(0); + expect(response.body.events[0].indexing_duration_ms).to.greaterThan(0); + expect(response.body.events[0].gap_duration_s).to.eql(0); + expect(response.body.events[0].security_status).to.eql('succeeded'); + expect(response.body.events[0].security_message).to.eql( + 'Rule execution completed successfully' + ); + const backfillStart = moment(fromManualRuleRun).add(5, 'm').toISOString(); + expect(response.body.events[0].backfill.to).to.eql(backfillStart); + expect(response.body.events[0].backfill.from).to.eql(fromManualRuleRun); + }); + + it('should reflect run_type_filters in params', async () => { + const rule = { + ...getRuleForAlertTesting(['auditbeat-*']), + query: 'process.executable: "/usr/bin/sudo"', + }; + const { id } = await createRule(supertest, log, rule); + const startManualRuleRun = dateMath.parse('now-1m')?.utc().toISOString() ?? ''; + const endManualRuleRun = dateMath.parse('now', { roundUp: true })?.utc().toISOString() ?? ''; + await manualRuleRun({ + ruleId: id, + supertest, + start: startManualRuleRun, + end: endManualRuleRun, + }); + await waitForRuleSuccess({ supertest, log, id }); + await waitForEventLogExecuteComplete(es, log, id, 1, 'execute-backfill'); + + const start = dateMath.parse('now-24h')?.utc().toISOString(); + const end = dateMath.parse('now', { roundUp: true })?.utc().toISOString(); + const responseWithAllRunTypes = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: [] }); + + expect(responseWithAllRunTypes.status).to.eql(200); + expect(responseWithAllRunTypes.body.total).to.eql(2); + + const responseWithOnlyStandard = await supertest + .get(getRuleExecutionResultsUrl(id)) + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .query({ start, end, run_type_filters: ['standard'] }); + + expect(responseWithOnlyStandard.status).to.eql(200); + expect(responseWithOnlyStandard.body.total).to.eql(1); + }); }); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts index b6750c9aa168a..0d702badf7536 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/get_event_log_execute_complete_by_id.ts @@ -19,7 +19,8 @@ import type { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; export const getEventLogExecuteCompleteById = async ( es: Client, log: ToolingLog, - ruleId: string + ruleId: string, + executionType: string = 'execute' ): Promise => { const response = await es.search({ index: '.kibana-event-log*', @@ -36,7 +37,7 @@ export const getEventLogExecuteCompleteById = async ( }, { match_phrase: { - 'event.action': 'execute', + 'event.action': executionType, }, }, { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts index 584fddced9db6..ca8941f1bdabd 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/event_log/wait_for_event_log_execute_complete.ts @@ -23,11 +23,12 @@ export const waitForEventLogExecuteComplete = async ( es: Client, log: ToolingLog, ruleId: string, - totalExecutions = 1 + totalExecutions = 1, + executionType = 'execute' ): Promise => { await waitFor( async () => { - const executionCount = await getEventLogExecuteCompleteById(es, log, ruleId); + const executionCount = await getEventLogExecuteCompleteById(es, log, ruleId, executionType); return executionCount >= totalExecutions; }, 'waitForEventLogExecuteComplete', diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts new file mode 100644 index 0000000000000..77c88c0b8573b --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { deleteAlertsAndRules } from '../../../../tasks/api_calls/common'; +import { login } from '../../../../tasks/login'; +import { visit } from '../../../../tasks/navigation'; +import { ruleDetailsUrl } from '../../../../urls/rule_details'; +import { createRule } from '../../../../tasks/api_calls/rules'; +import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule'; +import { + goToExecutionLogTab, + getExecutionLogTableRow, + refreshRuleExecutionTable, + filterByRunType, +} from '../../../../tasks/rule_details'; +import { getNewRule } from '../../../../objects/rule'; +import { EXECUTION_SHOWING } from '../../../../screens/rule_details'; +import { manualRuleRun } from '../../../../tasks/api_calls/backfill'; + +describe( + 'Event log', + { + tags: ['@ess', '@serverless'], + env: { + ftrConfig: { + kbnServerArgs: [ + `--xpack.securitySolution.enableExperimental=${JSON.stringify(['manualRuleRunEnabled'])}`, + ], + }, + }, + }, + function () { + before(() => { + login(); + deleteAlertsAndRules(); + createRule({ + ...getNewRule(), + }).then((rule) => { + cy.wrap(rule.body.id).as('ruleId'); + }); + }); + + it('should display the execution log', function () { + visit(ruleDetailsUrl(this.ruleId)); + waitForAlertsToPopulate(); + goToExecutionLogTab(); + + cy.get(EXECUTION_SHOWING).contains('Showing 1 rule execution'); + getExecutionLogTableRow().should('have.length', 1); + manualRuleRun({ + ruleId: this.ruleId, + start: moment().subtract(5, 'm').toISOString(), + end: moment().toISOString(), + }); + + cy.waitUntil( + () => { + cy.log('Waiting for assignees to appear in popover'); + refreshRuleExecutionTable(); + return getExecutionLogTableRow().then((rows) => { + return rows.length === 2; + }); + }, + { interval: 5000, timeout: 20000 } + ); + cy.get(EXECUTION_SHOWING).contains('Showing 2 rule executions'); + filterByRunType('Manual'); + + getExecutionLogTableRow().should('have.length', 1); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts index 15a4d1864e00f..f9c78f5167256 100644 --- a/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/screens/rule_details.ts @@ -163,3 +163,15 @@ export const MAX_SIGNALS_DETAILS = '[data-test-subj="maxSignalsPropertyValue"]'; export const DESCRIPTION_SETUP_GUIDE_BUTTON = '[data-test-subj="stepAboutDetailsToggle-setup"]'; export const DESCRIPTION_SETUP_GUIDE_CONTENT = '[data-test-subj="stepAboutDetailsSetupContent"]'; + +export const EXECUTIONS_TAB = 'a[data-test-subj="navigation-execution_results"]'; + +export const EXECUTION_SHOWING = `[data-test-subj="executionsShowing"]`; + +export const EXECUTION_TABLE = `[data-test-subj="executionsTable"]`; + +export const EXECUTION_LOG_CONTAINER = `[data-test-subj="executionLogContainer"]`; + +export const EXECUTION_RUN_TYPE_FILTER = `[data-test-subj="ExecutionRunTypeFilter"]`; + +export const EXECUTION_RUN_TYPE_FILTER_ITEM = `[data-test-subj="ExecutionRunTypeFilter-item"]`; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts new file mode 100644 index 0000000000000..9d14c5b8905fe --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/backfill.ts @@ -0,0 +1,33 @@ +/* + * 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 { INTERNAL_BASE_ALERTING_API_PATH } from '@kbn/alerting-plugin/common'; + +const BACKFILL_RULE_URL = `${INTERNAL_BASE_ALERTING_API_PATH}/rules/backfill`; +const BACKFILL_RULE_URL_SCHEDULE = `${BACKFILL_RULE_URL}/_schedule`; + +export const manualRuleRun = ({ + ruleId, + start, + end, +}: { + ruleId: string; + start: string; + end: string; +}): Cypress.Chainable> => { + return cy.request({ + method: 'POST', + url: BACKFILL_RULE_URL_SCHEDULE, + headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'Kibana' }, + body: [ + { + rule_id: ruleId, + start, + end, + }, + ], + }); +}; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts index b5b82d78783c4..aae7be96c33fb 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/rule_details.ts @@ -36,6 +36,11 @@ import { RULE_NAME_HEADER, INVESTIGATION_FIELDS_DETAILS, ABOUT_DETAILS, + EXECUTIONS_TAB, + EXECUTION_TABLE, + EXECUTION_LOG_CONTAINER, + EXECUTION_RUN_TYPE_FILTER, + EXECUTION_RUN_TYPE_FILTER_ITEM, } from '../screens/rule_details'; import { RuleDetailsTabs, ruleDetailsUrl } from '../urls/rule_details'; import { @@ -46,6 +51,7 @@ import { } from './exceptions'; import { addsFields, closeFieldsBrowser, filterFieldsBrowser } from './fields_browser'; import { visit } from './navigation'; +import { LOCAL_DATE_PICKER_APPLY_BUTTON_TIMELINE } from '../screens/date_picker'; interface VisitRuleDetailsPageOptions { tab?: RuleDetailsTabs; @@ -122,6 +128,10 @@ export const goToExceptionsTab = () => { cy.get(EXCEPTIONS_TAB).click(); }; +export const goToExecutionLogTab = () => { + cy.get(EXECUTIONS_TAB).click(); +}; + export const viewExpiredExceptionItems = () => { cy.get(EXCEPTIONS_TAB_EXPIRED_FILTER).click(); cy.get(EXCEPTIONS_TAB_ACTIVE_FILTER).click(); @@ -190,3 +200,13 @@ export const hasInvestigationFields = (fields: string) => { export const goToRuleEditSettings = () => { cy.get(EDIT_RULE_SETTINGS_LINK).click(); }; + +export const getExecutionLogTableRow = () => cy.get(EXECUTION_TABLE).find('tbody tr'); + +export const refreshRuleExecutionTable = () => + cy.get(`${EXECUTION_LOG_CONTAINER} ${LOCAL_DATE_PICKER_APPLY_BUTTON_TIMELINE}`).click(); + +export const filterByRunType = (ruleType: string) => { + cy.get(EXECUTION_RUN_TYPE_FILTER).click(); + cy.get(EXECUTION_RUN_TYPE_FILTER_ITEM).contains(ruleType).click(); +}; From 4396bf6e2eb3bbea440ed6a763e84a6b5e42efa4 Mon Sep 17 00:00:00 2001 From: Faisal Kanout Date: Tue, 28 May 2024 15:34:52 +0200 Subject: [PATCH 33/59] [OBS-UX-MNGMT] Move the Alerting comparators from TriggersActionsUI plugin to the alerting-types package (#181584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary It fixes #179633 Observability created a Comparator type/enum, when ResponseOps is already exporting one and other rules using it. The only difference is the wording of not in between [I put the two types side by side to compare] Currently, we import the one in triggers-actions-ui-plugin , and then update the not in between to match our Comparator. ### Comparing the two enums: ![Screenshot 2024-04-23 at 18 17 23](https://github.com/elastic/kibana/assets/6838659/16429ff9-e672-4c16-92ed-488a2f66007d) ## For reviewers 🧪 - Everything should work as expected: Alert flyout, Alert reason message, Rule creation flyout, etc. - I kept the `outside` comparator (replaced by `NOT BETWEEN`) for backward compatibility --- .github/CODEOWNERS | 1 + package.json | 1 + tsconfig.base.json | 2 + .../kbn-alerting-comparators/README.md | 5 + .../kbn-alerting-comparators/index.ts | 7 + .../kbn-alerting-comparators/jest.config.js | 12 + .../kbn-alerting-comparators/kibana.jsonc | 5 + .../kbn-alerting-comparators/package.json | 6 + .../src/comparators.ts | 20 ++ .../kbn-alerting-comparators/tsconfig.json | 18 + .../src/create_custom_threshold_rule.ts | 8 +- .../scenarios/custom_threshold_log_count.ts | 8 +- .../custom_threshold_log_count_groupby.ts | 8 +- .../custom_threshold_log_count_nodata.ts | 9 +- .../scenarios/custom_threshold_metric_avg.ts | 8 +- .../custom_threshold_metric_avg_groupby.ts | 8 +- .../custom_threshold_metric_avg_nodata.ts | 8 +- .../alerting_test_data/tsconfig.json | 1 + x-pack/plugins/alerting/tsconfig.json | 4 +- .../infra/common/alerting/metrics/types.ts | 47 +-- .../common/components/threshold.stories.tsx | 4 +- .../common/components/threshold.test.tsx | 4 +- .../alerting/common/components/threshold.tsx | 5 +- .../threshold_annotations.test.tsx | 14 +- .../threshold_annotations.tsx | 14 +- .../inventory/components/expression.test.tsx | 11 +- .../inventory/components/expression.tsx | 21 +- .../inventory/components/expression_chart.tsx | 5 +- .../inventory/components/validation.tsx | 6 +- .../custom_equation_editor.stories.tsx | 9 +- .../components/expression.test.tsx | 6 +- .../components/expression.tsx | 7 +- .../components/expression_chart.test.tsx | 5 +- .../components/expression_chart.tsx | 5 +- .../components/expression_row.test.tsx | 8 +- .../components/expression_row.tsx | 32 +- .../components/validation.tsx | 7 +- .../lib/generate_unique_key.test.ts | 10 +- .../mocks/metric_threshold_rule.ts | 17 +- .../server/lib/alerting/common/messages.ts | 52 ++- ...nventory_metric_threshold_executor.test.ts | 30 +- .../inventory_metric_threshold_executor.ts | 7 +- .../lib/create_bucket_selector.test.ts | 8 +- .../lib/create_bucket_selector.ts | 11 +- .../lib/create_condition_script.test.ts | 5 +- .../lib/create_condition_script.ts | 11 +- ...er_inventory_metric_threshold_rule_type.ts | 18 +- .../lib/create_bucket_selector.ts | 15 +- .../lib/create_condition_script.ts | 11 +- .../alerting/metric_threshold/lib/get_data.ts | 30 +- .../metric_threshold/lib/metric_query.test.ts | 9 +- .../metric_threshold_executor.test.ts | 340 +++++++++--------- .../metric_threshold_executor.ts | 44 ++- .../register_metric_threshold_rule_type.ts | 24 +- .../infra/tsconfig.json | 2 + .../common/custom_threshold_rule/types.ts | 15 +- .../observability/common/i18n.ts | 43 +++ .../observability/common/index.ts | 2 +- .../convert_legacy_outside_comparator.test.ts | 24 ++ .../convert_legacy_outside_comparator.ts | 17 + .../map_rules_params_with_flyout.test.ts | 46 ++- .../helpers/map_rules_params_with_flyout.ts | 172 ++++++--- .../alert_overview/overview_columns.tsx | 15 +- .../helpers/log_rate_analysis_query.test.ts | 9 +- .../threshold_annotations.test.tsx | 14 +- .../threshold_annotations.tsx | 14 +- .../custom_equation_editor.stories.tsx | 6 +- .../components/custom_threshold.stories.tsx | 5 +- .../components/custom_threshold.test.tsx | 6 +- .../components/custom_threshold.tsx | 4 +- .../components/expression_row.test.tsx | 7 +- .../components/expression_row.tsx | 35 +- .../rule_condition_chart.test.tsx | 4 +- .../rule_condition_chart.tsx | 16 +- .../components/validation.tsx | 6 +- .../custom_threshold_rule_expression.test.tsx | 9 +- .../custom_threshold_rule_expression.tsx | 5 +- .../mocks/custom_threshold_rule.ts | 23 +- .../pages/rule_details/rule_details.tsx | 1 - .../custom_threshold_executor.test.ts | 288 +++++++-------- .../custom_threshold_executor.ts | 7 +- .../lib/create_bucket_selector.ts | 6 +- .../lib/create_condition_script.ts | 10 +- .../rules/custom_threshold/lib/get_data.ts | 1 - .../custom_threshold/lib/metric_query.test.ts | 4 +- .../lib/rules/custom_threshold/messages.ts | 50 +-- .../mocks/custom_threshold_alert_result.ts | 8 +- .../mocks/custom_threshold_metric_params.ts | 10 +- .../register_custom_threshold_rule_type.ts | 7 +- .../rules/custom_threshold/translations.ts | 44 --- .../observability/tsconfig.json | 1 + .../evaluation/alert_templates/templates.ts | 13 +- .../tsconfig.json | 3 +- x-pack/plugins/rule_registry/tsconfig.json | 2 +- .../public/rule_types/es_query/constants.ts | 3 +- .../public/rule_types/es_query/validation.ts | 2 +- .../rule_types/threshold/expression.tsx | 2 +- .../rule_types/threshold/visualization.tsx | 3 +- x-pack/plugins/stack_alerts/tsconfig.json | 1 + x-pack/plugins/task_manager/tsconfig.json | 4 +- .../translations/translations/fr-FR.json | 2 - .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - .../plugins/triggers_actions_ui/kibana.jsonc | 5 +- .../public/common/constants/comparators.ts | 69 ---- .../public/common/constants/index.ts | 55 ++- .../expression_items/threshold.test.tsx | 2 +- .../common/expression_items/threshold.tsx | 2 +- .../public/common/index.ts | 8 +- .../public/common/types.ts | 6 - .../triggers_actions_ui/public/index.ts | 3 +- .../plugins/triggers_actions_ui/tsconfig.json | 3 +- .../custom_threshold_rule/avg_pct_fired.ts | 8 +- .../custom_threshold_rule/avg_pct_no_data.ts | 8 +- .../custom_threshold_rule/avg_us_fired.ts | 8 +- .../custom_eq_avg_bytes_fired.ts | 10 +- .../documents_count_fired.ts | 10 +- .../custom_threshold_rule/group_by_fired.ts | 8 +- .../custom_threshold_rule/p99_pct_fired.ts | 8 +- .../custom_threshold_rule/rate_bytes_fired.ts | 8 +- .../custom_threshold_rule_data_view.ts | 8 +- .../observability/metric_threshold_rule.ts | 6 +- .../metrics_ui/inventory_threshold_alert.ts | 7 +- .../apis/metrics_ui/metric_threshold_alert.ts | 38 +- .../apis/metrics_ui/metrics_alerting.ts | 5 +- .../infra/metrics_source_configuration.ts | 4 +- x-pack/test/tsconfig.json | 5 +- .../custom_threshold_rule/avg_pct_fired.ts | 8 +- .../custom_threshold_rule/avg_pct_no_data.ts | 8 +- .../custom_eq_avg_bytes_fired.ts | 10 +- .../documents_count_fired.ts | 10 +- .../custom_threshold_rule/group_by_fired.ts | 8 +- .../custom_threshold_rule/p99_pct_fired.ts | 8 +- x-pack/test_serverless/tsconfig.json | 1 + yarn.lock | 4 + 135 files changed, 1238 insertions(+), 1079 deletions(-) create mode 100644 x-pack/packages/kbn-alerting-comparators/README.md create mode 100644 x-pack/packages/kbn-alerting-comparators/index.ts create mode 100644 x-pack/packages/kbn-alerting-comparators/jest.config.js create mode 100644 x-pack/packages/kbn-alerting-comparators/kibana.jsonc create mode 100644 x-pack/packages/kbn-alerting-comparators/package.json create mode 100644 x-pack/packages/kbn-alerting-comparators/src/comparators.ts create mode 100644 x-pack/packages/kbn-alerting-comparators/tsconfig.json create mode 100644 x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts create mode 100644 x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts delete mode 100644 x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6535d9aabd147..3d9ca954ecb7d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -21,6 +21,7 @@ x-pack/plugins/aiops @elastic/ml-ui x-pack/packages/ml/aiops_test_utils @elastic/ml-ui x-pack/test/alerting_api_integration/packages/helpers @elastic/response-ops x-pack/test/alerting_api_integration/common/plugins/alerts @elastic/response-ops +x-pack/packages/kbn-alerting-comparators @elastic/response-ops x-pack/examples/alerting_example @elastic/response-ops x-pack/test/functional_with_es_ssl/plugins/alerts @elastic/response-ops x-pack/plugins/alerting @elastic/response-ops diff --git a/package.json b/package.json index ec56622c5324c..8d4fc2fce5b72 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,7 @@ "@kbn/aiops-plugin": "link:x-pack/plugins/aiops", "@kbn/aiops-test-utils": "link:x-pack/packages/ml/aiops_test_utils", "@kbn/alerting-api-integration-test-plugin": "link:x-pack/test/alerting_api_integration/common/plugins/alerts", + "@kbn/alerting-comparators": "link:x-pack/packages/kbn-alerting-comparators", "@kbn/alerting-example-plugin": "link:x-pack/examples/alerting_example", "@kbn/alerting-fixture-plugin": "link:x-pack/test/functional_with_es_ssl/plugins/alerts", "@kbn/alerting-plugin": "link:x-pack/plugins/alerting", diff --git a/tsconfig.base.json b/tsconfig.base.json index d5f8c82c63922..923d6dcb38c39 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -36,6 +36,8 @@ "@kbn/alerting-api-integration-helpers/*": ["x-pack/test/alerting_api_integration/packages/helpers/*"], "@kbn/alerting-api-integration-test-plugin": ["x-pack/test/alerting_api_integration/common/plugins/alerts"], "@kbn/alerting-api-integration-test-plugin/*": ["x-pack/test/alerting_api_integration/common/plugins/alerts/*"], + "@kbn/alerting-comparators": ["x-pack/packages/kbn-alerting-comparators"], + "@kbn/alerting-comparators/*": ["x-pack/packages/kbn-alerting-comparators/*"], "@kbn/alerting-example-plugin": ["x-pack/examples/alerting_example"], "@kbn/alerting-example-plugin/*": ["x-pack/examples/alerting_example/*"], "@kbn/alerting-fixture-plugin": ["x-pack/test/functional_with_es_ssl/plugins/alerts"], diff --git a/x-pack/packages/kbn-alerting-comparators/README.md b/x-pack/packages/kbn-alerting-comparators/README.md new file mode 100644 index 0000000000000..b3b6c0a02fe76 --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/README.md @@ -0,0 +1,5 @@ +# @kbn/alerting-comparators + +Contains type information and enum for the alerting rule comparators. e.g. >, < + +This comparators are used extensively in Observability UI and server side. Also, in the triggers-actions-ui in some related UI like ThresholdExpression. \ No newline at end of file diff --git a/x-pack/packages/kbn-alerting-comparators/index.ts b/x-pack/packages/kbn-alerting-comparators/index.ts new file mode 100644 index 0000000000000..148e0bad18141 --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export * from './src/comparators'; diff --git a/x-pack/packages/kbn-alerting-comparators/jest.config.js b/x-pack/packages/kbn-alerting-comparators/jest.config.js new file mode 100644 index 0000000000000..9f1da3b8875aa --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/jest.config.js @@ -0,0 +1,12 @@ +/* + * 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. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../../..', + roots: ['/x-pack/packages/kbn-alerting-comparators'], +}; diff --git a/x-pack/packages/kbn-alerting-comparators/kibana.jsonc b/x-pack/packages/kbn-alerting-comparators/kibana.jsonc new file mode 100644 index 0000000000000..94ac1e532ab1f --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/alerting-comparators", + "owner": "@elastic/response-ops" +} \ No newline at end of file diff --git a/x-pack/packages/kbn-alerting-comparators/package.json b/x-pack/packages/kbn-alerting-comparators/package.json new file mode 100644 index 0000000000000..d202ae5e6d75b --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/alerting-comparators", + "private": true, + "version": "1.0.0", + "license": "Elastic License 2.0" +} diff --git a/x-pack/packages/kbn-alerting-comparators/src/comparators.ts b/x-pack/packages/kbn-alerting-comparators/src/comparators.ts new file mode 100644 index 0000000000000..0568fa204df6c --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/src/comparators.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface Comparator { + text: string; + value: string; + requiredValues: number; +} +export enum COMPARATORS { + GREATER_THAN = '>', + GREATER_THAN_OR_EQUALS = '>=', + BETWEEN = 'between', + LESS_THAN = '<', + LESS_THAN_OR_EQUALS = '<=', + NOT_BETWEEN = 'notBetween', +} diff --git a/x-pack/packages/kbn-alerting-comparators/tsconfig.json b/x-pack/packages/kbn-alerting-comparators/tsconfig.json new file mode 100644 index 0000000000000..196ffd23d93cd --- /dev/null +++ b/x-pack/packages/kbn-alerting-comparators/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + ] +} diff --git a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts index 7ad732af36948..1cd65cc9e7415 100644 --- a/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts +++ b/x-pack/packages/observability/alerting_test_data/src/create_custom_threshold_rule.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { FIRED_ACTIONS_ID } from './constants'; import { createRule } from './create_rule'; @@ -38,7 +36,7 @@ export const createCustomThresholdRule = async ( params: { criteria: ruleParams.params?.criteria || [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts index be2f2e344d7a5..da3d03218dd80 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; export const scenario1 = { dataView: { @@ -22,7 +20,7 @@ export const scenario1 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [100], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts index fe8e2cfa01d32..bc8c7c5d2e310 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_groupby.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario2 = { dataView: { @@ -22,7 +20,7 @@ export const scenario2 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [40], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts index bbb4d4e1c2551..3131c04a126b7 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_log_count_nodata.ts @@ -5,11 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; - +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario3 = { dataView: { indexPattern: 'high-cardinality-data-fake_hosts.fake_hosts-*', @@ -22,7 +19,7 @@ export const scenario3 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [5], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts index dcf6048915ebf..82cb220678ec6 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario4 = { dataView: { @@ -22,7 +20,7 @@ export const scenario4 = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [80], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts index b42006a33a652..5ef69a10dc654 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_groupby.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario5 = { dataView: { @@ -22,7 +20,7 @@ export const scenario5 = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [80], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts index 9bea0a00eb714..2a92d6f8440f2 100644 --- a/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts +++ b/x-pack/packages/observability/alerting_test_data/src/scenarios/custom_threshold_metric_avg_nodata.ts @@ -5,10 +5,8 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export const scenario6 = { dataView: { @@ -22,7 +20,7 @@ export const scenario6 = { params: { criteria: [ { - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/packages/observability/alerting_test_data/tsconfig.json b/x-pack/packages/observability/alerting_test_data/tsconfig.json index d2a9a55a887de..109b0dfbcf1a4 100644 --- a/x-pack/packages/observability/alerting_test_data/tsconfig.json +++ b/x-pack/packages/observability/alerting_test_data/tsconfig.json @@ -18,5 +18,6 @@ "kbn_references": [ "@kbn/observability-plugin", "@kbn/rule-data-utils", + "@kbn/alerting-comparators", ] } diff --git a/x-pack/plugins/alerting/tsconfig.json b/x-pack/plugins/alerting/tsconfig.json index 1bc43c34e5aa8..63d1ea5768c4e 100644 --- a/x-pack/plugins/alerting/tsconfig.json +++ b/x-pack/plugins/alerting/tsconfig.json @@ -39,7 +39,6 @@ "@kbn/data-views-plugin", "@kbn/share-plugin", "@kbn/safer-lodash-set", - "@kbn/alerting-state-types", "@kbn/alerting-types", "@kbn/alerts-as-data-utils", "@kbn/core-elasticsearch-client-server-mocks", @@ -70,7 +69,8 @@ "@kbn/core-execution-context-server-mocks", "@kbn/react-kibana-context-render", "@kbn/search-types", - "@kbn/core-security-server", + "@kbn/alerting-state-types", + "@kbn/core-security-server" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts index 9121ec11adfab..b5a3c084c47cb 100644 --- a/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts +++ b/x-pack/plugins/observability_solution/infra/common/alerting/metrics/types.ts @@ -4,10 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import * as rt from 'io-ts'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { ML_ANOMALY_THRESHOLD } from '@kbn/ml-anomaly-utils/anomaly_threshold'; import { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { SnapshotCustomMetricInput } from '../../http_api'; export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold'; @@ -18,20 +18,6 @@ export enum InfraRuleType { InventoryThreshold = 'metrics.alert.inventory.threshold', } -export interface InfraRuleTypeParams { - [InfraRuleType.MetricThreshold]: MetricThresholdParams; - [InfraRuleType.InventoryThreshold]: InventoryMetricConditions; -} - -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} - export enum Aggregators { COUNT = 'count', AVERAGE = 'avg', @@ -53,27 +39,6 @@ export enum AlertStates { ERROR, } -const metricAnomalyNodeTypeRT = rt.union([rt.literal('hosts'), rt.literal('k8s')]); -const metricAnomalyMetricRT = rt.union([ - rt.literal('memory_usage'), - rt.literal('network_in'), - rt.literal('network_out'), -]); -const metricAnomalyInfluencerFilterRT = rt.type({ - fieldName: rt.string, - fieldValue: rt.string, -}); - -export interface MetricAnomalyParams { - nodeType: rt.TypeOf; - metric: rt.TypeOf; - alertInterval?: string; - sourceId?: string; - spaceId?: string; - threshold: Exclude; - influencerFilter: rt.TypeOf | undefined; -} - // Types for the executor export interface InventoryMetricConditions { @@ -82,10 +47,10 @@ export interface InventoryMetricConditions { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; + comparator: COMPARATORS | LEGACY_COMPARATORS; customMetric?: SnapshotCustomMetricInput; warningThreshold?: number[]; - warningComparator?: Comparator; + warningComparator?: COMPARATORS | LEGACY_COMPARATORS; } export interface InventoryMetricThresholdParams { @@ -111,8 +76,8 @@ interface BaseMetricExpressionParams { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; - warningComparator?: Comparator; + comparator: COMPARATORS | LEGACY_COMPARATORS; + warningComparator?: COMPARATORS | LEGACY_COMPARATORS; warningThreshold?: number[]; } diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx index 73361902799c6..0b253a981b122 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.stories.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ComponentMeta } from '@storybook/react'; import { LIGHT_THEME } from '@elastic/charts'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Props, Threshold as Component } from './threshold'; export default { @@ -30,7 +30,7 @@ export default { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: 90, title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx index b78216b78f60c..3decd9cfdd9c0 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.test.tsx @@ -6,7 +6,7 @@ */ import { LIGHT_THEME } from '@elastic/charts'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { render } from '@testing-library/react'; import { Props, Threshold } from './threshold'; import React from 'react'; @@ -15,7 +15,7 @@ describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: 90, title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx index d9ca396f9aa33..bb710a165733b 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/components/threshold.tsx @@ -10,8 +10,7 @@ import { Chart, Metric, Settings } from '@elastic/charts'; import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; import type { PartialTheme, Theme } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/alerting/metrics'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; export interface ChartProps { theme?: PartialTheme; baseTheme: Theme; @@ -19,7 +18,7 @@ export interface ChartProps { export interface Props { chartProps: ChartProps; - comparator: Comparator | string; + comparator: COMPARATORS | string; id: string; threshold: number; title: string; diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx index be8e474b60114..a5dd3386fc083 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.test.tsx @@ -6,7 +6,7 @@ */ import { shallow } from 'enzyme'; import React from 'react'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color } from '../../../../common/color_palette'; import { ThresholdAnnotations } from './threshold_annotations'; @@ -29,7 +29,7 @@ describe('ThresholdAnnotations', () => { const defaultProps = { threshold: [20, 30], sortedThresholds: [20, 30], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, color: Color.color0, id: 'testId', firstTimestamp: 123456789, @@ -53,7 +53,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for in between thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.BETWEEN }); + const wrapper = await setup({ comparator: COMPARATORS.BETWEEN }); const annotation = wrapper.find('[data-test-subj="between-rect"]'); const expectedValues = [ @@ -72,7 +72,7 @@ describe('ThresholdAnnotations', () => { }); it('should render an upper rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-lower-rect"]'); const expectedValues = [ @@ -91,7 +91,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a lower rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-upper-rect"]'); const expectedValues = [ @@ -110,7 +110,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for below thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.LT }); + const wrapper = await setup({ comparator: COMPARATORS.LESS_THAN }); const annotation = wrapper.find('[data-test-subj="below-rect"]'); const expectedValues = [ @@ -129,7 +129,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for above thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.GT }); + const wrapper = await setup({ comparator: COMPARATORS.GREATER_THAN }); const annotation = wrapper.find('[data-test-subj="above-rect"]'); const expectedValues = [ diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx index 9400537bb9d7c..82258a493537f 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/common/criterion_preview_chart/threshold_annotations.tsx @@ -7,13 +7,13 @@ import { AnnotationDomainType, LineAnnotation, RectAnnotation } from '@elastic/charts'; import { first, last } from 'lodash'; import React from 'react'; -import { Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color, colorTransformer } from '../../../../common/color_palette'; interface ThresholdAnnotationsProps { threshold: number[]; sortedThresholds: number[]; - comparator: Comparator; + comparator: COMPARATORS; color: Color; id: string; firstTimestamp: number; @@ -34,8 +34,10 @@ export const ThresholdAnnotations = ({ domain, }: ThresholdAnnotationsProps) => { if (!comparator || !threshold) return null; - const isAbove = [Comparator.GT, Comparator.GT_OR_EQ].includes(comparator); - const isBelow = [Comparator.LT, Comparator.LT_OR_EQ].includes(comparator); + const isAbove = [COMPARATORS.GREATER_THAN, COMPARATORS.GREATER_THAN_OR_EQUALS].includes( + comparator + ); + const isBelow = [COMPARATORS.LESS_THAN, COMPARATORS.LESS_THAN_OR_EQUALS].includes(comparator); return ( <> - {sortedThresholds.length === 2 && comparator === Comparator.BETWEEN ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.BETWEEN ? ( <> ) : null} - {sortedThresholds.length === 2 && comparator === Comparator.OUTSIDE_RANGE ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.NOT_BETWEEN ? ( <> { expect(ruleParams.criteria).toEqual([ { metric: 'memory', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -131,7 +132,7 @@ describe('Expression', () => { timeSize: 1, timeUnit: 'm', threshold: [10], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], nodeType: undefined, @@ -173,7 +174,7 @@ describe('Expression', () => { expect(ruleParams.criteria).toEqual([ { metric: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -217,7 +218,7 @@ describe('ExpressionRow', () => { } const expression = { metric: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx index ce88a2112e528..b3b62cabcb9ed 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/inventory/components/expression.tsx @@ -52,16 +52,17 @@ import { SnapshotMetricType, SnapshotMetricTypeRT, } from '@kbn/metrics-data-access-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { + SnapshotCustomMetricInput, + SnapshotCustomMetricInputRT, +} from '../../../../common/http_api'; import { - Comparator, FilterQuery, InventoryMetricConditions, QUERY_INVALID, } from '../../../../common/alerting/metrics'; -import { - SnapshotCustomMetricInput, - SnapshotCustomMetricInputRT, -} from '../../../../common/http_api/snapshot_api'; import { toMetricOpt } from '../../../../common/snapshot_metric_i18n'; import { useMetricsDataViewContext, @@ -106,7 +107,7 @@ type Props = Omit< export const defaultExpression = { metric: 'cpu' as SnapshotMetricType, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -447,7 +448,7 @@ export const ExpressionRow: FC> = (props) const { children, setRuleParams, expression, errors, expressionId, remove, canDelete } = props; const { metric, - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [], customMetric, warningThreshold = [], @@ -478,14 +479,14 @@ export const ExpressionRow: FC> = (props) const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator | undefined }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS | undefined }); }, [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -713,7 +714,7 @@ const ThresholdElement: React.FC<{ <>
= ({ stack={false} /> = ({ /> {expression.warningComparator && expression.warningThreshold && ( { expect(ruleParams.criteria).toEqual([ { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -110,7 +110,7 @@ describe('Expression', () => { }, { metric: 'system.cpu.user.pct', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx index 542dd74eb1732..c6afc61ebf881 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression.tsx @@ -28,12 +28,13 @@ import { RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; import { TimeUnitChar } from '@kbn/observability-plugin/common/utils/formatters/duration'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, QUERY_INVALID } from '../../../../common/alerting/metrics'; import { useMetricsDataViewContext, useSourceContext, withSourceProvider, } from '../../../containers/metrics_source'; -import { Aggregators, Comparator, QUERY_INVALID } from '../../../../common/alerting/metrics'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; @@ -57,7 +58,7 @@ type Props = Omit< const defaultExpression = { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize: 1, timeUnit: 'm', @@ -182,7 +183,7 @@ export const Expressions: React.FC = (props) => { 'criteria', md.currentOptions.metrics.map((metric) => ({ metric: metric.field, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [], timeSize, timeUnit, diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx index bb0ecd1697924..7e775dba71d9f 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx @@ -11,7 +11,8 @@ import { LineAnnotation, RectAnnotation } from '@elastic/charts'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; // We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` import { coreMock as mockCoreMock } from '@kbn/core/public/mocks'; -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { Aggregators } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { MetricExpression } from '../types'; import type { DataView } from '@kbn/data-views-plugin/common'; import { ExpressionChart } from './expression_chart'; @@ -105,7 +106,7 @@ describe('ExpressionChart', () => { timeUnit: 'm', sourceId: 'default', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, }; const { wrapper } = await setup(expression); expect(wrapper.find('[data-test-subj~="noChartData"]').exists()).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx index 495a5f8a2d468..42baee346c75b 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -22,6 +22,7 @@ import { useActiveCursor } from '@kbn/charts-plugin/public'; import { first, last } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; import { useTimelineChartTheme } from '../../../utils/use_timeline_chart_theme'; import { Color } from '../../../../common/color_palette'; import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api'; @@ -155,7 +156,7 @@ export const ExpressionChart: React.FC = ({ stack={false} /> = ({ /> {expression.warningComparator && expression.warningThreshold && ( { it('should display thresholds as a percentage for pct metrics', async () => { const expression = { metric: 'system.cpu.user.pct', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', @@ -107,7 +107,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a decimal for all other metrics', async () => { const expression = { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', @@ -125,7 +125,7 @@ describe('ExpressionRow', () => { it('should render a helpText for the of expression', async () => { const expression = { metric: 'system.load.1', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx index a3a5c2021307d..cca94d10e9539 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -21,31 +21,21 @@ import React, { PropsWithChildren, useCallback, useMemo, useState } from 'react' import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { AggregationType, - builtInComparators, IErrorObject, OfExpression, ThresholdExpression, WhenExpression, } from '@kbn/triggers-actions-ui-plugin/public'; import useToggle from 'react-use/lib/useToggle'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators } from '../../../../common/alerting/metrics'; import { useMetricsDataViewContext } from '../../../containers/metrics_source'; -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; import { decimalToPct, pctToDecimal } from '../../../../common/utils/corrected_percent_convert'; import { AGGREGATION_TYPES, MetricExpression } from '../types'; import { CustomEquationEditor } from './custom_equation'; import { CUSTOM_EQUATION } from '../i18n_strings'; -const customComparators = { - ...builtInComparators, - [Comparator.OUTSIDE_RANGE]: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.outsideRangeLabel', { - defaultMessage: 'Is not between', - }), - value: Comparator.OUTSIDE_RANGE, - requiredValues: 2, - }, -}; - interface ExpressionRowProps { expressionId: number; expression: MetricExpression; @@ -81,7 +71,7 @@ export const ExpressionRow = ({ const { aggType = AGGREGATION_TYPES.MAX, metric, - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [], warningThreshold = [], warningComparator, @@ -115,14 +105,14 @@ export const ExpressionRow = ({ const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); const updateWarningComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, warningComparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, warningComparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -373,14 +363,18 @@ const ThresholdElement: React.FC<{ if (isMetricPct) return threshold.map((v) => decimalToPct(v)); return threshold; }, [threshold, isMetricPct]); - + const thresholdComparator = useCallback(() => { + if (!comparator) return COMPARATORS.GREATER_THAN; + // Check if the rule had the legacy OUTSIDE_RANGE inside its params. + // Then, change it on-the-fly to NOT_BETWEEN + return convertToBuiltInComparators(comparator); + }, [comparator]); return ( <> |<|=|\?|\:|&|\!|\|]+/g; const isCustomMetricExpressionParams = ( @@ -118,7 +117,7 @@ export function validateMetricThreshold({ // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold, type } = props as { - comparator?: Comparator; + comparator?: COMPARATORS; threshold?: number[]; type: 'critical' | 'warning'; }; @@ -135,7 +134,7 @@ export function validateMetricThreshold({ }); } - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + if (comparator === COMPARATORS.BETWEEN && (!threshold || threshold.length < 2)) { errors[id][type].threshold1.push( i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { defaultMessage: 'Threshold is required.', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts index a9460e700f38b..a47bc517676a9 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/lib/generate_unique_key.test.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../common/alerting/metrics'; import { MetricExpression } from '../types'; import { generateUniqueKey } from './generate_unique_key'; @@ -14,7 +14,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.COUNT, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [2000, 5000], timeSize: 15, timeUnit: 'm', @@ -24,7 +24,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.CUSTOM, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [30], timeSize: 15, timeUnit: 'm', @@ -34,7 +34,7 @@ describe('generateUniqueKey', () => { [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [500], timeSize: 15, timeUnit: 'm', diff --git a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts index ed0d4444f023a..0ef6478ff12d7 100644 --- a/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/infra/public/alerting/metric_threshold/mocks/metric_threshold_rule.ts @@ -6,7 +6,8 @@ */ import { v4 as uuidv4 } from 'uuid'; -import { Aggregators, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../common/alerting/metrics'; import { MetricThresholdAlert, MetricThresholdRule } from '../components/alert_details_app_section'; export const buildMetricThresholdRule = ( @@ -59,24 +60,24 @@ export const buildMetricThresholdRule = ( criteria: [ { aggType: Aggregators.COUNT, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [2000], timeSize: 15, timeUnit: 'm', }, { aggType: Aggregators.MAX, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [4], timeSize: 15, timeUnit: 'm', metric: 'system.cpu.user.pct', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, { aggType: Aggregators.MIN, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.8], timeSize: 15, timeUnit: 'm', @@ -131,7 +132,7 @@ export const buildMetricThresholdAlert = ( criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [2000], timeSize: 15, timeUnit: 'm', @@ -139,12 +140,12 @@ export const buildMetricThresholdAlert = ( }, { aggType: Aggregators.MAX, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [4], timeSize: 15, timeUnit: 'm', metric: 'system.cpu.user.pct', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, ], diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts index 2caf83d309861..5060e6e0a6587 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/common/messages.ts @@ -10,7 +10,8 @@ import { formatDurationFromTimeUnitChar, TimeUnitChar, } from '@kbn/observability-plugin/common/utils/formatters/duration'; -import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { AlertStates } from '../../../../common/alerting/metrics'; import { UNGROUPED_FACTORY_KEY } from './utils'; export const DOCUMENT_COUNT_I18N = i18n.translate( @@ -49,7 +50,7 @@ const toNumber = (value: number | string) => typeof value === 'string' ? parseFloat(value) : value; const recoveredComparatorToI18n = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], currentValue: number ) => { @@ -60,20 +61,49 @@ const recoveredComparatorToI18n = ( defaultMessage: 'above', }); switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return currentValue < threshold[0] ? belowText : aboveText; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return i18n.translate('xpack.infra.metrics.alerting.threshold.betweenRecovery', { defaultMessage: 'between', }); - case Comparator.GT: - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return belowText; - case Comparator.LT: - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN: + case COMPARATORS.LESS_THAN_OR_EQUALS: return aboveText; } }; +const alertComparatorToI18n = (comparator: COMPARATORS) => { + switch (comparator) { + case COMPARATORS.BETWEEN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.between', { + defaultMessage: 'between', + }); + case COMPARATORS.NOT_BETWEEN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.notBetween', { + defaultMessage: 'not between', + }); + case COMPARATORS.GREATER_THAN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.above', { + defaultMessage: 'above', + }); + case COMPARATORS.GREATER_THAN_OR_EQUALS: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.aboveOrEqual', { + defaultMessage: 'above or equal', + }); + case COMPARATORS.LESS_THAN: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.below', { + defaultMessage: 'below', + }); + + case COMPARATORS.LESS_THAN_OR_EQUALS: + return i18n.translate('xpack.infra.customThreshold.rule.threshold.belowOrEqual', { + defaultMessage: 'below or equal', + }); + } +}; const thresholdToI18n = ([a, b]: Array) => { if (typeof b === 'undefined') return a; @@ -88,7 +118,7 @@ const formatGroup = (group: string) => (group === UNGROUPED_FACTORY_KEY ? '' : ` export const buildFiredAlertReason: (alertResult: { group: string; metric: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; timeSize: number; @@ -100,7 +130,7 @@ export const buildFiredAlertReason: (alertResult: { values: { group: formatGroup(group), metric, - comparator, + comparator: alertComparatorToI18n(comparator), threshold: thresholdToI18n(threshold), currentValue, duration: formatDurationFromTimeUnitChar(timeSize, timeUnit), @@ -111,7 +141,7 @@ export const buildFiredAlertReason: (alertResult: { export const buildRecoveredAlertReason: (alertResult: { group: string; metric: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; }) => string = ({ group, metric, comparator, threshold, currentValue }) => diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts index b0ffe0b354b13..d109669aa90f0 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.test.ts @@ -13,12 +13,8 @@ import { RuleExecutorServicesMock, alertsMock } from '@kbn/alerting-plugin/serve import { LifecycleAlertServices } from '@kbn/rule-registry-plugin/server'; import { ruleRegistryMocks } from '@kbn/rule-registry-plugin/server/mocks'; import { createLifecycleRuleExecutorMock } from '@kbn/rule-registry-plugin/server/utils/create_lifecycle_rule_executor_mock'; -import { - Aggregators, - Comparator, - InventoryMetricConditions, -} from '../../../../common/alerting/metrics'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, InventoryMetricConditions } from '../../../../common/alerting/metrics'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; import { createInventoryMetricThresholdExecutor } from './inventory_metric_threshold_executor'; @@ -178,7 +174,7 @@ const baseCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as InventoryMetricConditions; describe('The inventory threshold alert type', () => { @@ -187,7 +183,7 @@ describe('The inventory threshold alert type', () => { setup(); - const execute = (comparator: Comparator, threshold: number[], options?: any) => + const execute = (comparator: COMPARATORS, threshold: number[], options?: any) => executor({ ...mockOptions, services, @@ -215,7 +211,7 @@ describe('The inventory threshold alert type', () => { test('throws error when alertsClient is null', async () => { try { services.alertsClient = null; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); } catch (e) { expect(e).toMatchInlineSnapshot( '[Error: Expected alertsClient not to be null! There may have been an issue installing alert resources.]' @@ -232,7 +228,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -248,7 +244,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -259,7 +255,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(mostRecentAction(instanceIdA).tags).toStrictEqual([ 'host-01_tag1', 'host-01_tag2', @@ -282,7 +278,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -298,7 +294,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -309,7 +305,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(mostRecentAction(instanceIdA).tags).toStrictEqual(['ruleTag1', 'ruleTag2']); expect(mostRecentAction(instanceIdB).tags).toStrictEqual(['ruleTag1', 'ruleTag2']); }); @@ -329,7 +325,7 @@ describe('The inventory threshold alert type', () => { timeSize: 1, timeUnit: 'm', threshold: [0.75], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, shouldFire: true, shouldWarn: false, currentValue: 1.0, @@ -340,7 +336,7 @@ describe('The inventory threshold alert type', () => { }, }, }); - await execute(Comparator.GT, [0.75], options); + await execute(COMPARATORS.GREATER_THAN, [0.75], options); expect(evaluateConditionFn).toHaveBeenCalledWith( expect.objectContaining({ executionTimestamp: mockedEndDate, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts index 7124ce9db597f..ad1056b65f0cc 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -19,7 +19,7 @@ import { AlertInstanceState as AlertState, } from '@kbn/alerting-plugin/common'; import { AlertsClientError, RuleExecutorOptions, RuleTypeState } from '@kbn/alerting-plugin/server'; -import { getAlertUrl } from '@kbn/observability-plugin/common'; +import { convertToBuiltInComparators, getAlertUrl } from '@kbn/observability-plugin/common'; import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; import { ObservabilityMetricsAlert } from '@kbn/alerts-as-data-utils'; import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; @@ -128,7 +128,6 @@ export const createInventoryMetricThresholdExecutor = }); const indexedStartedAt = start ?? startedAt.toISOString(); - alertsClient.setAlertData({ id: UNGROUPED_FACTORY_KEY, payload: { @@ -403,7 +402,9 @@ const buildReasonWithVerboseMetricName = ( : resultItem.metric), currentValue: formatMetric(resultItem.metric, resultItem.currentValue), threshold: formatThreshold(resultItem.metric, thresholdToFormat), - comparator: useWarningThreshold ? resultItem.warningComparator! : resultItem.comparator, + comparator: useWarningThreshold + ? convertToBuiltInComparators(resultItem.warningComparator!) + : convertToBuiltInComparators(resultItem.comparator), }; return buildReason(resultWithVerboseMetricName); }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts index 279793a4a4102..a7ddc75eb30d6 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.test.ts @@ -4,8 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Comparator, InventoryMetricConditions } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { InventoryMetricConditions } from '../../../../../common/alerting/metrics'; import { createBucketSelector } from './create_bucket_selector'; describe('createBucketSelector', () => { @@ -15,9 +15,9 @@ describe('createBucketSelector', () => { timeSize: 5, timeUnit: 'm', threshold: [8], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, warningThreshold: [16], - warningComparator: Comparator.LT_OR_EQ, + warningComparator: COMPARATORS.LESS_THAN_OR_EQUALS, }; expect(createBucketSelector('tx', inventoryMetricConditions)).toEqual({ selectedBucket: { diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts index 7392446000db6..440e39ed14723 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_bucket_selector.ts @@ -6,7 +6,8 @@ */ import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; -import { Comparator, InventoryMetricConditions } from '../../../../../common/alerting/metrics'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { InventoryMetricConditions } from '../../../../../common/alerting/metrics'; import { SnapshotCustomMetricInput } from '../../../../../common/http_api'; import { createConditionScript } from './create_condition_script'; @@ -34,7 +35,7 @@ export const createBucketSelector = ( }, script: createConditionScript( condition.warningThreshold as number[], - condition.warningComparator as Comparator, + convertToBuiltInComparators(condition.warningComparator!), metric ), }, @@ -47,7 +48,11 @@ export const createBucketSelector = ( buckets_path: { value: metricId, }, - script: createConditionScript(condition.threshold, condition.comparator, metric), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator), + metric + ), }, } : EMPTY_SHOULD_WARN; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts index f93989f2d4b9d..fdc1738875153 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.test.ts @@ -4,13 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { Comparator } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createConditionScript } from './create_condition_script'; describe('createConditionScript', () => { it('should convert tx threshold from bits to byte', () => { - expect(createConditionScript([8], Comparator.GT_OR_EQ, 'tx')).toEqual({ + expect(createConditionScript([8], COMPARATORS.GREATER_THAN_OR_EQUALS, 'tx')).toEqual({ params: { // Threshold has been converted from 8 bits to 1 byte threshold: 1, diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts index a62f5d92dac06..21f84eb612475 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/lib/create_condition_script.ts @@ -5,16 +5,16 @@ * 2.0. */ import { SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; -import { Comparator } from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { convertMetricValue } from './convert_metric_value'; export const createConditionScript = ( conditionThresholds: number[], - comparator: Comparator, + comparator: COMPARATORS, metric: SnapshotMetricType ) => { const threshold = conditionThresholds.map((n) => convertMetricValue(metric, n)); - if (comparator === Comparator.BETWEEN && threshold.length === 2) { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -23,9 +23,10 @@ export const createConditionScript = ( }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts index b3ba3c5b184a9..4f124b8327841 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_rule_type.ts @@ -16,15 +16,14 @@ import { SnapshotMetricType, SnapshotMetricTypeKeys, } from '@kbn/metrics-data-access-plugin/common'; -import type { InfraConfig } from '../../../../common/plugin_config_types'; -import { - Comparator, - METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, -} from '../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { SnapshotCustomAggregation, SNAPSHOT_CUSTOM_AGGREGATIONS, -} from '../../../../common/http_api/snapshot_api'; +} from '../../../../common/http_api'; +import type { InfraConfig } from '../../../../common/plugin_config_types'; +import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; import { InfraBackendLibs } from '../../infra_types'; import { alertDetailUrlActionVariableDescription, @@ -54,16 +53,15 @@ import { import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; import { O11Y_AAD_FIELDS } from '../../../../common/constants'; +const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const condition = schema.object({ threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)) as Type, + comparator: oneOfLiterals(comparators) as Type, timeUnit: schema.string() as Type, timeSize: schema.number(), metric: oneOfLiterals(Object.keys(SnapshotMetricTypeKeys)) as Type, warningThreshold: schema.maybe(schema.arrayOf(schema.number())), - warningComparator: schema.maybe(oneOfLiterals(Object.values(Comparator))) as Type< - Comparator | undefined - >, + warningComparator: schema.maybe(oneOfLiterals(comparators)) as Type, customMetric: schema.maybe( schema.object({ type: schema.literal('custom'), diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts index 25e725d7d0a62..2aa2ef6b5c838 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_bucket_selector.ts @@ -4,12 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { createConditionScript } from './create_condition_script'; import { createLastPeriod } from './wrap_in_period'; @@ -49,7 +45,7 @@ export const createBucketSelector = ( }, script: createConditionScript( condition.warningThreshold as number[], - condition.warningComparator as Comparator + convertToBuiltInComparators(condition.warningComparator!) ), }, } @@ -60,7 +56,10 @@ export const createBucketSelector = ( buckets_path: { value: bucketPath, }, - script: createConditionScript(condition.threshold, condition.comparator), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator) + ), }, }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts index 1320607685a87..9b56baa0dcba5 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/create_condition_script.ts @@ -4,10 +4,9 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { Comparator } from '../../../../../common/alerting/metrics'; - -export const createConditionScript = (threshold: number[], comparator: Comparator) => { - if (comparator === Comparator.BETWEEN && threshold.length === 2) { +import { COMPARATORS } from '@kbn/alerting-comparators'; +export const createConditionScript = (threshold: number[], comparator: COMPARATORS) => { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -16,9 +15,9 @@ export const createConditionScript = (threshold: number[], comparator: Comparato }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts index 0053d41795d42..7e97c59549624 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/get_data.ts @@ -9,11 +9,9 @@ import { SearchResponse, AggregationsAggregate } from '@elastic/elasticsearch/li import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import { EcsFieldsResponse } from '@kbn/rule-registry-plugin/common/search_strategy'; -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { AdditionalContext, doFieldsExist, @@ -226,10 +224,16 @@ export const getData = async ( // the value will end up being ZERO, for other metrics it will be null. In this case // we need to do the evaluation in Node.js if (aggs.all && params.aggType === Aggregators.COUNT && value === 0) { - const trigger = comparatorMap[params.comparator](value, params.threshold); + const trigger = comparatorMap[convertToBuiltInComparators(params.comparator)]( + value, + params.threshold + ); const warn = params.warningThreshold && params.warningComparator - ? comparatorMap[params.warningComparator](value, params.warningThreshold) + ? comparatorMap[convertToBuiltInComparators(params.warningComparator)]( + value, + params.warningThreshold + ) : false; return { [UNGROUPED_FACTORY_KEY]: { @@ -286,13 +290,13 @@ export const getData = async ( }; const comparatorMap = { - [Comparator.BETWEEN]: (value: number, [a, b]: number[]) => + [COMPARATORS.BETWEEN]: (value: number, [a, b]: number[]) => value >= Math.min(a, b) && value <= Math.max(a, b), // `threshold` is always an array of numbers in case the BETWEEN comparator is // used; all other compartors will just destructure the first value in the array - [Comparator.GT]: (a: number, [b]: number[]) => a > b, - [Comparator.LT]: (a: number, [b]: number[]) => a < b, - [Comparator.OUTSIDE_RANGE]: (value: number, [a, b]: number[]) => value < a || value > b, - [Comparator.GT_OR_EQ]: (a: number, [b]: number[]) => a >= b, - [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, + [COMPARATORS.GREATER_THAN]: (a: number, [b]: number[]) => a > b, + [COMPARATORS.LESS_THAN]: (a: number, [b]: number[]) => a < b, + [COMPARATORS.NOT_BETWEEN]: (value: number, [a, b]: number[]) => value < a || value > b, + [COMPARATORS.GREATER_THAN_OR_EQUALS]: (a: number, [b]: number[]) => a >= b, + [COMPARATORS.LESS_THAN_OR_EQUALS]: (a: number, [b]: number[]) => a <= b, }; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts index 381465fa198d9..a32c924385dbc 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/lib/metric_query.test.ts @@ -6,11 +6,8 @@ */ import moment from 'moment'; -import { - Aggregators, - Comparator, - MetricExpressionParams, -} from '../../../../../common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators, MetricExpressionParams } from '../../../../../common/alerting/metrics'; import { getElasticsearchMetricQuery } from './metric_query'; describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { @@ -20,7 +17,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const groupBy = 'host.doggoname'; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index eeb7f61b99ddf..9cc5712ce09b6 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -8,9 +8,9 @@ import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; import { getThresholds } from '../common/get_values'; import { set } from '@kbn/safer-lodash-set'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CountMetricExpressionParams, NonCountMetricExpressionParams, } from '../../../../common/alerting/metrics'; @@ -109,7 +109,7 @@ describe('The metric threshold rule type', () => { afterAll(() => jest.useRealTimers()); describe('querying the entire infrastructure', () => { - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -125,7 +125,7 @@ describe('The metric threshold rule type', () => { }, }); const setResults = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], shouldFire: boolean = false, shouldWarn: boolean = false, @@ -149,8 +149,8 @@ describe('The metric threshold rule type', () => { ]); test('should report alert with the > comparator when condition is met', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -159,20 +159,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.75.', tags: [], }); }); test('should not report any alerts with the > comparator when condition is not met', async () => { - setResults(Comparator.GT, [1.5], false); - await execute(Comparator.GT, [1.5]); + setResults(COMPARATORS.GREATER_THAN, [1.5], false); + await execute(COMPARATORS.GREATER_THAN, [1.5]); testNAlertsReported(0); }); test('should report alert with the < comparator when condition is met', async () => { - setResults(Comparator.LT, [1.5], true); - await execute(Comparator.LT, [1.5]); + setResults(COMPARATORS.LESS_THAN, [1.5], true); + await execute(COMPARATORS.LESS_THAN, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -181,20 +181,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when < 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when below 1.5.', tags: [], }); }); test('should not report any alerts with the < comparator when condition is not met', async () => { - setResults(Comparator.LT, [0.75], false); - await execute(Comparator.LT, [0.75]); + setResults(COMPARATORS.LESS_THAN, [0.75], false); + await execute(COMPARATORS.LESS_THAN, [0.75]); testNAlertsReported(0); }); test('should report alert with the >= comparator when condition is met', async () => { - setResults(Comparator.GT_OR_EQ, [0.75], true); - await execute(Comparator.GT_OR_EQ, [0.75]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -203,20 +203,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when >= 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above or equal 0.75.', tags: [], }); }); test('should not report any alerts with the >= comparator when condition is not met', async () => { - setResults(Comparator.GT_OR_EQ, [1.5], false); - await execute(Comparator.GT_OR_EQ, [1.5]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5], false); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5]); testNAlertsReported(0); }); test('should report alert with the <= comparator when condition is met', async () => { - setResults(Comparator.LT_OR_EQ, [1.5], true); - await execute(Comparator.LT_OR_EQ, [1.5]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -225,20 +225,20 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when <= 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when below or equal 1.5.', tags: [], }); }); test('should not report any alerts with the <= comparator when condition is not met', async () => { - setResults(Comparator.LT_OR_EQ, [0.75], false); - await execute(Comparator.LT_OR_EQ, [0.75]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75], false); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75]); testNAlertsReported(0); }); test('should report alert with the between comparator when condition is met', async () => { - setResults(Comparator.BETWEEN, [0, 1.5], true); - await execute(Comparator.BETWEEN, [0, 1.5]); + setResults(COMPARATORS.BETWEEN, [0, 1.5], true); + await execute(COMPARATORS.BETWEEN, [0, 1.5]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -253,14 +253,14 @@ describe('The metric threshold rule type', () => { }); test('should not report any alerts with the between comparator when condition is not met', async () => { - setResults(Comparator.BETWEEN, [0, 0.75], false); - await execute(Comparator.BETWEEN, [0, 0.75]); + setResults(COMPARATORS.BETWEEN, [0, 0.75], false); + await execute(COMPARATORS.BETWEEN, [0, 0.75]); testNAlertsReported(0); }); test('should report alert with the outside range comparator when condition is met', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 0.75], true); - await execute(Comparator.OUTSIDE_RANGE, [0, 0.75]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 0.75], true); + await execute(COMPARATORS.NOT_BETWEEN, [0, 0.75]); testNAlertsReported(1); testAlertReported(1, { id: '*', @@ -269,21 +269,21 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when outside 0 and 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when not between 0 and 0.75.', tags: [], }); }); test('should not report any alerts with the outside range comparator when condition is not met', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 1.5], false); - await execute(Comparator.OUTSIDE_RANGE, [0, 1.5]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 1.5], false); + await execute(COMPARATORS.NOT_BETWEEN, [0, 1.5]); testNAlertsReported(0); }); }); describe('querying with a groupBy parameter', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['something'], metric?: string, @@ -313,7 +313,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -325,7 +325,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -337,7 +337,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, @@ -346,7 +346,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.75.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -357,7 +357,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for b. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for b. Alert when above 0.75.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -368,7 +368,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], metric: 'test.metric.1', currentValue: 1.0, @@ -380,7 +380,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], metric: 'test.metric.1', currentValue: 3, @@ -392,7 +392,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [1.5]); + await execute(COMPARATORS.LESS_THAN, [1.5]); testNAlertsReported(1); testAlertReported(1, { id: alertIdA, @@ -401,7 +401,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when < 1.5.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when below 1.5.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -412,7 +412,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], metric: 'test.metric.1', currentValue: 1.0, @@ -424,7 +424,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], metric: 'test.metric.1', currentValue: 3, @@ -436,7 +436,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [5]); + await execute(COMPARATORS.GREATER_THAN, [5]); testNAlertsReported(0); }); @@ -445,7 +445,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -457,7 +457,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -469,7 +469,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -482,7 +482,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something'], 'test.metric.2' @@ -492,7 +492,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -504,7 +504,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -516,7 +516,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -529,7 +529,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something'], 'test.metric.1', @@ -542,7 +542,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -554,7 +554,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -567,7 +567,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult3 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['something', 'something-else'], 'test.metric.1', @@ -577,7 +577,7 @@ describe('The metric threshold rule type', () => { }); const executeWithFilter = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], filterQuery: string, metric?: string, @@ -606,7 +606,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -618,7 +618,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -630,7 +630,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -643,7 +643,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -653,7 +653,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -665,7 +665,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -677,7 +677,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -690,7 +690,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -703,7 +703,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -715,7 +715,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -728,7 +728,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'different' }), 'test.metric.1', @@ -742,7 +742,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 1.0, @@ -754,7 +754,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -766,7 +766,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.2', currentValue: 3, @@ -779,7 +779,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -789,7 +789,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -801,7 +801,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -813,7 +813,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -826,7 +826,7 @@ describe('The metric threshold rule type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -840,7 +840,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -852,7 +852,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: null, @@ -867,7 +867,7 @@ describe('The metric threshold rule type', () => { // Consider c as untracked services.alertsClient.isTrackedAlert.mockImplementation((id: string) => id !== 'c'); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -882,7 +882,7 @@ describe('The metric threshold rule type', () => { describe('querying with a groupBy parameter host.name and rule tags', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['host.name'], metric?: string, @@ -916,7 +916,7 @@ describe('The metric threshold rule type', () => { { 'host-01': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -931,7 +931,7 @@ describe('The metric threshold rule type', () => { }, 'host-02': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 3, @@ -946,7 +946,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, @@ -955,7 +955,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for host-01. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min for host-01. Alert when above 0.75.', tags: ['host-01_tag1', 'host-01_tag2', 'ruleTag1', 'ruleTag2'], groupByKeys: { host: { name: alertIdA } }, group: [{ field: 'host.name', value: alertIdA }], @@ -967,7 +967,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for host-02. Alert when > 0.75.', + reason: 'test.metric.1 is 3 in the last 1 min for host-02. Alert when above 0.75.', tags: ['host-02_tag1', 'host-02_tag2', 'ruleTag1', 'ruleTag2'], groupByKeys: { host: { name: alertIdB } }, group: [{ field: 'host.name', value: alertIdB }], @@ -977,7 +977,7 @@ describe('The metric threshold rule type', () => { describe('querying without a groupBy parameter and rule tags', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string = '', metric?: string, @@ -1009,7 +1009,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metric: 'test.metric.1', currentValue: 1.0, @@ -1023,7 +1023,7 @@ describe('The metric threshold rule type', () => { ]); const alertID = '*'; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1032,7 +1032,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.75.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.75.', tags: ['ruleTag1', 'ruleTag2'], }); }); @@ -1040,7 +1040,7 @@ describe('The metric threshold rule type', () => { describe('querying with multiple criteria', () => { const execute = ( - comparator: Comparator, + comparator: COMPARATORS, thresholdA: number[], thresholdB: number[], groupBy: string = '', @@ -1073,7 +1073,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1087,7 +1087,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 3.0, @@ -1100,7 +1100,7 @@ describe('The metric threshold rule type', () => { }, ]); const alertID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1111,7 +1111,7 @@ describe('The metric threshold rule type', () => { actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', reason: - 'test.metric.1 is 1 in the last 1 min. Alert when >= 1.\ntest.metric.2 is 3 in the last 1 min. Alert when >= 3.', + 'test.metric.1 is 1 in the last 1 min. Alert when above or equal 1.\ntest.metric.2 is 3 in the last 1 min. Alert when above or equal 3.', tags: [], }); }); @@ -1121,7 +1121,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1134,7 +1134,7 @@ describe('The metric threshold rule type', () => { }, {}, ]); - await execute(Comparator.LT_OR_EQ, [1.0], [2.5]); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], [2.5]); testNAlertsReported(0); }); @@ -1143,7 +1143,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 1.0, @@ -1155,7 +1155,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], metric: 'test.metric.1', currentValue: 3.0, @@ -1169,7 +1169,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 3.0, @@ -1181,7 +1181,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metric: 'test.metric.2', currentValue: 1.0, @@ -1194,7 +1194,7 @@ describe('The metric threshold rule type', () => { }, ]); const alertIdA = 'a'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'something'); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0], 'something'); testNAlertsReported(1); testAlertReported(1, { id: alertIdA, @@ -1205,7 +1205,7 @@ describe('The metric threshold rule type', () => { actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', reason: - 'test.metric.1 is 1 in the last 1 min for a. Alert when >= 1.\ntest.metric.2 is 3 in the last 1 min for a. Alert when >= 3.', + 'test.metric.1 is 1 in the last 1 min for a. Alert when above or equal 1.\ntest.metric.2 is 3 in the last 1 min for a. Alert when above or equal 3.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1214,7 +1214,7 @@ describe('The metric threshold rule type', () => { describe('querying with the count aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1235,7 +1235,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], metric: 'count', currentValue: 1, @@ -1247,14 +1247,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); testNAlertsReported(1); testAlertReported(1, { id: alertID, conditions: [{ metric: 'count', threshold: [0.9], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 1 in the last 1 min. Alert when > 0.9.', + reason: 'count is 1 in the last 1 min. Alert when above 0.9.', tags: [], }); @@ -1262,7 +1262,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.5], metric: 'count', currentValue: 1, @@ -1274,14 +1274,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [0.5]); + await execute(COMPARATORS.LESS_THAN, [0.5]); // should still have only been called once testNAlertsReported(1); }); describe('with a groupBy parameter', () => { const executeGroupBy = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], sourceId: string = 'default', state?: any @@ -1310,7 +1310,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 1, @@ -1322,7 +1322,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 1, @@ -1334,13 +1334,13 @@ describe('The metric threshold rule type', () => { }, }, ]); - const resultState = await executeGroupBy(Comparator.LT_OR_EQ, [0]); + const resultState = await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0]); testNAlertsReported(0); setEvaluationResults([ { a: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 0, @@ -1352,7 +1352,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], metric: 'count', currentValue: 0, @@ -1364,14 +1364,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await executeGroupBy(Comparator.LT_OR_EQ, [0], 'empty-response', resultState); + await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0], 'empty-response', resultState); testNAlertsReported(2); testAlertReported(1, { id: alertIdA, conditions: [{ metric: 'count', threshold: [0], value: '0', evaluation_value: 0 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 0 in the last 1 min for a. Alert when <= 0.', + reason: 'count is 0 in the last 1 min for a. Alert when below or equal 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1380,7 +1380,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'count', threshold: [0], value: '0', evaluation_value: 0 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'count is 0 in the last 1 min for b. Alert when <= 0.', + reason: 'count is 0 in the last 1 min for b. Alert when below or equal 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1389,7 +1389,7 @@ describe('The metric threshold rule type', () => { }); describe('querying with the p99 aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1411,7 +1411,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.2', currentValue: 3, @@ -1423,14 +1423,14 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [1]); + await execute(COMPARATORS.GREATER_THAN, [1]); testNAlertsReported(1); testAlertReported(1, { id: alertID, conditions: [{ metric: 'test.metric.2', threshold: [1], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min. Alert when > 1.', + reason: 'test.metric.2 is 3 in the last 1 min. Alert when above 1.', tags: [], }); @@ -1438,7 +1438,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.2', currentValue: 3, @@ -1450,7 +1450,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [1]); + await execute(COMPARATORS.LESS_THAN, [1]); // should still only have been called once testNAlertsReported(1); }); @@ -1458,7 +1458,7 @@ describe('The metric threshold rule type', () => { describe('querying with the p95 aggregator', () => { const alertID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1480,7 +1480,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.25], metric: 'test.metric.1', currentValue: 1.0, @@ -1492,7 +1492,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.GT, [0.25]); + await execute(COMPARATORS.GREATER_THAN, [0.25]); testNAlertsReported(1); testAlertReported(1, { id: alertID, @@ -1501,7 +1501,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min. Alert when > 0.25.', + reason: 'test.metric.1 is 1 in the last 1 min. Alert when above 0.25.', tags: [], }); @@ -1509,7 +1509,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.95], metric: 'test.metric.1', currentValue: 1.0, @@ -1521,7 +1521,7 @@ describe('The metric threshold rule type', () => { }, }, ]); - await execute(Comparator.LT, [0.95]); + await execute(COMPARATORS.LESS_THAN, [0.95]); // should still only have been called once testNAlertsReported(1); }); @@ -1538,7 +1538,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.3', }, @@ -1552,7 +1552,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1583,7 +1583,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1611,13 +1611,13 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metric: 'test.metric.3', }, { ...baseCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [30], }, ], @@ -1630,7 +1630,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metric: 'test.metric.3', currentValue: null, @@ -1681,7 +1681,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric, }, @@ -1706,7 +1706,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1736,7 +1736,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1766,7 +1766,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1.0, @@ -1778,7 +1778,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -1797,7 +1797,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1806,7 +1806,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1822,7 +1822,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1834,7 +1834,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -1877,7 +1877,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 3, @@ -1889,7 +1889,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 1, @@ -1901,7 +1901,7 @@ describe('The metric threshold rule type', () => { }, c: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.2', currentValue: 3, @@ -1920,7 +1920,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.2 is 3 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1929,7 +1929,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 1 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.2 is 1 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -1938,7 +1938,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.2', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.2 is 3 in the last 1 min for c. Alert when > 0.', + reason: 'test.metric.2 is 3 in the last 1 min for c. Alert when above 0.', tags: [], groupByKeys: { something: alertIdC }, }); @@ -1947,7 +1947,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1, @@ -1959,7 +1959,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -1978,7 +1978,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '1', evaluation_value: 1 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -1987,7 +1987,7 @@ describe('The metric threshold rule type', () => { conditions: [{ metric: 'test.metric.1', threshold: [0], value: '3', evaluation_value: 3 }], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -2004,7 +2004,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric, }, @@ -2025,7 +2025,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2043,7 +2043,7 @@ describe('The metric threshold rule type', () => { { '*': { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2061,7 +2061,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 1, @@ -2073,7 +2073,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.1', currentValue: 3, @@ -2094,7 +2094,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when > 0.', + reason: 'test.metric.1 is 1 in the last 1 min for a. Alert when above 0.', tags: [], groupByKeys: { something: alertIdA }, }); @@ -2105,7 +2105,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: FIRED_ACTIONS.id, alertState: 'ALERT', - reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when > 0.', + reason: 'test.metric.1 is 3 in the last 1 min for b. Alert when above 0.', tags: [], groupByKeys: { something: alertIdB }, }); @@ -2119,7 +2119,7 @@ describe('The metric threshold rule type', () => { { a: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2131,7 +2131,7 @@ describe('The metric threshold rule type', () => { }, b: { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metric: 'test.metric.3', currentValue: null, @@ -2226,7 +2226,7 @@ describe('The metric threshold rule type', () => { criteria: [ { ...baseNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [9.999], }, ], @@ -2234,9 +2234,9 @@ describe('The metric threshold rule type', () => { }); const setResults = ({ - comparator = Comparator.GT, + comparator = COMPARATORS.GREATER_THAN, threshold = [9999], - warningComparator = Comparator.GT, + warningComparator = COMPARATORS.GREATER_THAN, warningThreshold = [2.49], metric = 'test.metric.1', currentValue = 7.59, @@ -2278,7 +2278,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: WARNING_ACTIONS.id, alertState: 'WARNING', - reason: 'test.metric.1 is 2.5 in the last 1 min. Alert when > 2.49.', + reason: 'test.metric.1 is 2.5 in the last 1 min. Alert when above 2.49.', tags: [], }); @@ -2311,7 +2311,7 @@ describe('The metric threshold rule type', () => { ], actionGroup: WARNING_ACTIONS.id, alertState: 'WARNING', - reason: 'system.cpu.user.pct is 82% in the last 1 min. Alert when > 81%.', + reason: 'system.cpu.user.pct is 82% in the last 1 min. Alert when above 81%.', tags: [], }); }); @@ -2498,7 +2498,7 @@ const baseNonCountCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as NonCountMetricExpressionParams; const baseCountCriterion = { @@ -2506,5 +2506,5 @@ const baseCountCriterion = { timeSize: 1, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, } as CountMetricExpressionParams; diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 4ccd94f8560d2..49129e2058cc1 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -20,11 +20,12 @@ import { RecoveredActionGroup, } from '@kbn/alerting-plugin/common'; import { AlertsClientError, RuleExecutorOptions, RuleTypeState } from '@kbn/alerting-plugin/server'; -import type { TimeUnitChar } from '@kbn/observability-plugin/common'; -import { getAlertUrl } from '@kbn/observability-plugin/common'; +import { TimeUnitChar, getAlertUrl } from '@kbn/observability-plugin/common'; import { ObservabilityMetricsAlert } from '@kbn/alerts-as-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import { getOriginalActionGroup } from '../../../utils/get_original_action_group'; -import { AlertStates, Comparator } from '../../../../common/alerting/metrics'; +import { AlertStates } from '../../../../common/alerting/metrics'; import { createFormatter } from '../../../../common/formatters'; import { InfraBackendLibs } from '../../infra_types'; import { @@ -296,7 +297,16 @@ export const createMetricThresholdExecutor = reason = alertResults .map((result) => buildFiredAlertReason({ - ...formatAlertResult(result[group], nextState === AlertStates.WARNING), + ...formatAlertResult( + { + ...result[group], + comparator: convertToBuiltInComparators(result[group].comparator), + warningComparator: result[group].comparator + ? convertToBuiltInComparators(result[group].comparator) + : undefined, + }, + nextState === AlertStates.WARNING + ), group, }) ) @@ -367,21 +377,33 @@ export const createMetricThresholdExecutor = }), reason, threshold: mapToConditionsLookup(alertResults, (result, index) => { - const evaluation = result[group]; + const evaluation = result[group] as Evaluation; if (!evaluation) { return criteria[index].threshold; } - return formatAlertResult(evaluation).threshold; + return formatAlertResult({ + ...evaluation, + comparator: convertToBuiltInComparators(evaluation.comparator), + warningComparator: evaluation.warningComparator + ? convertToBuiltInComparators(evaluation.warningComparator) + : undefined, + }).threshold; }), timestamp, value: mapToConditionsLookup(alertResults, (result, index) => { - const evaluation = result[group]; + const evaluation = result[group] as Evaluation; if (!evaluation && criteria[index].aggType === 'count') { return 0; } else if (!evaluation) { return null; } - return formatAlertResult(evaluation).currentValue; + return formatAlertResult({ + ...evaluation, + comparator: convertToBuiltInComparators(evaluation.comparator), + warningComparator: evaluation.warningComparator + ? convertToBuiltInComparators(evaluation.warningComparator) + : undefined, + }).currentValue; }), viewInAppUrl: getMetricsViewInAppUrlWithSpaceId({ basePath: libs.basePath, @@ -518,9 +540,9 @@ const formatAlertResult = ( metric: string; currentValue: number | null; threshold: number[]; - comparator: Comparator; + comparator: COMPARATORS; warningThreshold?: number[]; - warningComparator?: Comparator; + warningComparator?: COMPARATORS; timeSize: number; timeUnit: TimeUnitChar; } & AlertResult, @@ -544,7 +566,7 @@ const formatAlertResult = ( threshold: Array.isArray(thresholdToFormat) ? thresholdToFormat.map((v: number) => formatter(v)) : formatter(thresholdToFormat), - comparator: comparatorToUse, + comparator: convertToBuiltInComparators(comparatorToUse), }; } diff --git a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts index 1adcd58f42592..8bd8aafcbf207 100644 --- a/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/infra/server/lib/alerting/metric_threshold/register_metric_threshold_rule_type.ts @@ -8,15 +8,12 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core/server'; import { schema } from '@kbn/config-schema'; import { i18n } from '@kbn/i18n'; -import { ActionGroupIdsOf } from '@kbn/alerting-plugin/common'; -import { - GetViewInAppRelativeUrlFnOpts, - PluginSetupContract, - RuleType, -} from '@kbn/alerting-plugin/server'; +import { GetViewInAppRelativeUrlFnOpts, PluginSetupContract } from '@kbn/alerting-plugin/server'; import { observabilityPaths } from '@kbn/observability-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '@kbn/observability-plugin/common/utils/convert_legacy_outside_comparator'; import type { InfraConfig } from '../../../../common/plugin_config_types'; -import { Comparator, METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; +import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../common/alerting/metrics'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api'; import { InfraBackendLibs } from '../../infra_types'; import { @@ -48,13 +45,6 @@ import { import { MetricsRulesTypeAlertDefinition } from '../register_rule_types'; import { O11Y_AAD_FIELDS } from '../../../../common/constants'; -type MetricThresholdAllowedActionGroups = ActionGroupIdsOf< - typeof FIRED_ACTIONS | typeof WARNING_ACTIONS | typeof NO_DATA_ACTIONS ->; -export type MetricThresholdAlertType = Omit & { - ActionGroupIdsOf: MetricThresholdAllowedActionGroups; -}; - export function registerMetricThresholdRuleType( alertingPlugin: PluginSetupContract, libs: InfraBackendLibs, @@ -63,14 +53,14 @@ export function registerMetricThresholdRuleType( if (!featureFlags.metricThresholdAlertRuleEnabled) { return; } - + const comparator = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const baseCriterion = { threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)), + comparator: oneOfLiterals(comparator), timeUnit: schema.string(), timeSize: schema.number(), warningThreshold: schema.maybe(schema.arrayOf(schema.number())), - warningComparator: schema.maybe(oneOfLiterals(Object.values(Comparator))), + warningComparator: schema.maybe(oneOfLiterals(comparator)), }; const nonCountCriterion = schema.object({ diff --git a/x-pack/plugins/observability_solution/infra/tsconfig.json b/x-pack/plugins/observability_solution/infra/tsconfig.json index d43ab3b5ccc46..63350d0cd9786 100644 --- a/x-pack/plugins/observability_solution/infra/tsconfig.json +++ b/x-pack/plugins/observability_solution/infra/tsconfig.json @@ -97,6 +97,8 @@ "@kbn/dashboard-plugin", "@kbn/shared-svg", "@kbn/aiops-log-rate-analysis", + "@kbn/search-types", + "@kbn/alerting-comparators", "@kbn/react-hooks", "@kbn/search-types", "@kbn/router-utils", diff --git a/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts b/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts index 38b7e6d90061c..0e4a0e7167069 100644 --- a/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts +++ b/x-pack/plugins/observability_solution/observability/common/custom_threshold_rule/types.ts @@ -8,6 +8,8 @@ import * as rt from 'io-ts'; import { DataViewSpec, SerializedSearchSourceFields } from '@kbn/data-plugin/common'; import { Filter, Query } from '@kbn/es-query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '../utils/convert_legacy_outside_comparator'; import { TimeUnitChar } from '../utils/formatters/duration'; export const ThresholdFormatterTypeRT = rt.keyof({ @@ -20,15 +22,6 @@ export const ThresholdFormatterTypeRT = rt.keyof({ }); export type ThresholdFormatterType = rt.TypeOf; -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} - export enum Aggregators { COUNT = 'count', AVERAGE = 'avg', @@ -78,9 +71,7 @@ export interface BaseMetricExpressionParams { timeUnit: TimeUnitChar; sourceId?: string; threshold: number[]; - comparator: Comparator; - warningComparator?: Comparator; - warningThreshold?: number[]; + comparator: COMPARATORS | LEGACY_COMPARATORS; } export interface CustomThresholdExpressionMetric { diff --git a/x-pack/plugins/observability_solution/observability/common/i18n.ts b/x-pack/plugins/observability_solution/observability/common/i18n.ts index 73c27e811b2d6..3e7badbf635cc 100644 --- a/x-pack/plugins/observability_solution/observability/common/i18n.ts +++ b/x-pack/plugins/observability_solution/observability/common/i18n.ts @@ -10,3 +10,46 @@ import { i18n } from '@kbn/i18n'; export const NOT_AVAILABLE_LABEL = i18n.translate('xpack.observability.notAvailable', { defaultMessage: 'N/A', }); + +// Comparators +export const BELOW_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.below', + { + defaultMessage: 'below', + } +); + +export const BELOW_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.belowOrEqual', + { + defaultMessage: 'below or equal', + } +); + +export const ABOVE_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.above', + { + defaultMessage: 'above', + } +); + +export const ABOVE_OR_EQ_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.aboveOrEqual', + { + defaultMessage: 'above or equal', + } +); + +export const BETWEEN_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.between', + { + defaultMessage: 'between', + } +); + +export const NOT_BETWEEN_TEXT = i18n.translate( + 'xpack.observability.customThreshold.rule.threshold.notBetween', + { + defaultMessage: 'not between', + } +); diff --git a/x-pack/plugins/observability_solution/observability/common/index.ts b/x-pack/plugins/observability_solution/observability/common/index.ts index d7d5672b95ec3..eb8049f2182e2 100644 --- a/x-pack/plugins/observability_solution/observability/common/index.ts +++ b/x-pack/plugins/observability_solution/observability/common/index.ts @@ -18,7 +18,7 @@ export { } from './utils/formatters'; export { getInspectResponse } from './utils/get_inspect_response'; export { getAlertDetailsUrl, getAlertUrl } from './utils/alerting/alert_url'; - +export { convertToBuiltInComparators } from './utils/convert_legacy_outside_comparator'; export { ProcessorEvent } from './processor_event'; export { diff --git a/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts new file mode 100644 index 0000000000000..70153c2084d6e --- /dev/null +++ b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.test.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + convertToBuiltInComparators, + LEGACY_COMPARATORS, +} from './convert_legacy_outside_comparator'; +import { COMPARATORS } from '@kbn/alerting-comparators'; + +describe('convertToBuiltInComparators', () => { + it('should return in between when passing the legacy outside', () => { + const comparator = convertToBuiltInComparators(LEGACY_COMPARATORS.OUTSIDE_RANGE); + expect(comparator).toBe(COMPARATORS.NOT_BETWEEN); + }); + + it('should return the same comparator when NOT passing the legacy outside', () => { + const comparator = convertToBuiltInComparators(COMPARATORS.GREATER_THAN); + expect(comparator).toBe(COMPARATORS.GREATER_THAN); + }); +}); diff --git a/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts new file mode 100644 index 0000000000000..d415e1a73187c --- /dev/null +++ b/x-pack/plugins/observability_solution/observability/common/utils/convert_legacy_outside_comparator.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { COMPARATORS } from '@kbn/alerting-comparators'; + +export enum LEGACY_COMPARATORS { + OUTSIDE_RANGE = 'outside', +} +export type LegacyComparator = COMPARATORS | LEGACY_COMPARATORS; + +export function convertToBuiltInComparators(comparator: LegacyComparator): COMPARATORS { + if (comparator === LEGACY_COMPARATORS.OUTSIDE_RANGE) return COMPARATORS.NOT_BETWEEN; + return comparator; +} diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts index 706fce0c75a62..d02961e45d001 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts @@ -103,13 +103,13 @@ describe('Map rules params with flyout', () => { excludeHitsFromPreviousRun: false, }, 'kibana.alert.evaluation.value': 100870655162.18182, - 'kibana.alert.evaluation.threshold': 1, + 'kibana.alert.evaluation.threshold': [1], }, }, results: [ { observedValue: [100870655162.18182], - threshold: [1], + threshold: '1', comparator: '>', pctAboveThreshold: ' (10087065516118.18% above the threshold)', }, @@ -149,7 +149,7 @@ describe('Map rules params with flyout', () => { observedValue: [4577], threshold: [100], comparator: 'more than', - pctAboveThreshold: ' (4477% above the threshold)', + pctAboveThreshold: ' (4477% more than the threshold)', }, ], }, @@ -215,12 +215,50 @@ describe('Map rules params with flyout', () => { results: [ { observedValue: '10.4 Mbit', - threshold: ['3 Mbit'], + threshold: '3 Mbit', comparator: '>', pctAboveThreshold: ' (247.54% above the threshold)', }, ], }, + { + ruleType: 'metrics.alert.inventory.threshold', + alert: { + fields: { + 'kibana.alert.rule.rule_type_id': 'metrics.alert.inventory.threshold', + 'kibana.alert.rule.parameters': { + nodeType: 'host', + criteria: [ + { + metric: 'rx', + comparator: '<', + threshold: [90], + timeSize: 1, + timeUnit: 'm', + customMetric: { + type: 'custom', + id: 'alert-custom-metric', + field: 'system.memory.used.pct', + aggregation: 'avg', + }, + }, + ], + sourceId: 'default', + }, + + 'kibana.alert.evaluation.value': [130.4], + 'kibana.alert.evaluation.threshold': 3000000, + }, + }, + results: [ + { + comparator: '<', + observedValue: '13,040%', + pctAboveThreshold: ' (14388.89% below the threshold)', + threshold: '9,000%', + }, + ], + }, { ruleType: 'apm.error_rate', alert: { diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts index f2f58e17ea56a..fce4bd35dbdee 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts @@ -19,7 +19,15 @@ import { } from '@kbn/rule-data-utils'; import { EsQueryRuleParams } from '@kbn/stack-alerts-plugin/public/rule_types/es_query/types'; import { i18n } from '@kbn/i18n'; -import { asDuration, asPercent } from '../../../../common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; + +import { + ABOVE_OR_EQ_TEXT, + ABOVE_TEXT, + BELOW_OR_EQ_TEXT, + BELOW_TEXT, +} from '../../../../common/i18n'; +import { asDuration, asPercent, convertToBuiltInComparators } from '../../../../common'; import { createFormatter } from '../../../../common/custom_threshold_rule/formatters'; import { metricValueFormatter } from '../../../../common/custom_threshold_rule/metric_value_formatter'; import { METRIC_FORMATTERS } from '../../../../common/custom_threshold_rule/formatters/snapshot_metric_formats'; @@ -37,12 +45,34 @@ export interface FlyoutThresholdData { pctAboveThreshold: string; } -const getPctAboveThreshold = (observedValue?: number, threshold?: number[]): string => { +const getI18nComparator = (comparator?: COMPARATORS) => { + switch (comparator) { + case COMPARATORS.GREATER_THAN: + return ABOVE_TEXT; + case COMPARATORS.GREATER_THAN_OR_EQUALS: + return ABOVE_OR_EQ_TEXT; + case COMPARATORS.LESS_THAN: + return BELOW_TEXT; + case COMPARATORS.LESS_THAN_OR_EQUALS: + return BELOW_OR_EQ_TEXT; + default: + return comparator; + } +}; +const getPctAboveThreshold = ( + threshold: number[], + comparator: COMPARATORS, + observedValue?: number +): string => { if (!observedValue || !threshold || threshold.length > 1 || threshold[0] <= 0) return ''; + return i18n.translate('xpack.observability.alertFlyout.overview.aboveThresholdLabel', { - defaultMessage: ' ({pctValue}% above the threshold)', + defaultMessage: ' ({pctValue}% {comparator} the threshold)', values: { - pctValue: parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)), + pctValue: Math.abs( + parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)) + ), + comparator: getI18nComparator(convertToBuiltInComparators(comparator)), }, }); }; @@ -78,7 +108,11 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] observedValue: formattedValue, threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, threshold), + pctAboveThreshold: getPctAboveThreshold( + threshold, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); @@ -113,46 +147,82 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] observedValue: formattedValue, threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, threshold), + pctAboveThreshold: getPctAboveThreshold( + threshold, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); case METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID: return observedValues.map((observedValue, metricIndex) => { - const criteria = ruleCriteria[metricIndex] as BaseMetricExpressionParams & { + const { threshold, customMetric, metric, comparator } = ruleCriteria[ + metricIndex + ] as BaseMetricExpressionParams & { metric: string; + customMetric: { + field: string; + }; }; - const infraType = METRIC_FORMATTERS[criteria.metric].formatter; - const formatter = createFormatter(infraType); - const comparator = criteria.comparator; - const threshold = criteria.threshold; - const formatThreshold = threshold.map((v: number) => { - if (infraType === 'percent') { - v = Number(v) / 100; + const metricField = customMetric?.field || metric; + const thresholdFormatted = threshold.map((thresholdToFormat) => { + if ( + metricField.endsWith('.pct') || + (METRIC_FORMATTERS[metric] && METRIC_FORMATTERS[metric].formatter === 'percent') + ) { + thresholdToFormat = thresholdToFormat / 100; + } else if ( + metricField.endsWith('.bytes') || + (METRIC_FORMATTERS[metric] && METRIC_FORMATTERS[metric].formatter === 'bits') + ) { + thresholdToFormat = thresholdToFormat / 8; } - if (infraType === 'bits') { - v = Number(v) / 8; - } - return v; + return thresholdToFormat; }); + let observedValueFormatted: string; + let thresholdFormattedAsString: string; + if (customMetric.field) { + observedValueFormatted = metricValueFormatter( + observedValue as number, + customMetric.field + ); + thresholdFormattedAsString = threshold + .map((thresholdToStringFormat) => + metricValueFormatter(thresholdToStringFormat, metricField) + ) + .join(' AND '); + } else { + const infraType = METRIC_FORMATTERS[metric].formatter; + const formatter = createFormatter(infraType); + observedValueFormatted = formatter(observedValue); + thresholdFormattedAsString = thresholdFormatted.map(formatter).join(' AND '); + } + return { - observedValue: formatter(observedValue), - threshold: formatThreshold.map(formatter), + observedValue: observedValueFormatted, + threshold: thresholdFormattedAsString, comparator, - pctAboveThreshold: getPctAboveThreshold(observedValue, formatThreshold), + pctAboveThreshold: getPctAboveThreshold( + thresholdFormatted, + convertToBuiltInComparators(comparator), + observedValue + ), } as unknown as FlyoutThresholdData; }); case LOG_THRESHOLD_ALERT_TYPE_ID: - const { comparator } = ruleParams?.count as { comparator: string }; + const { comparator } = ruleParams?.count as { comparator: COMPARATORS }; const flyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], comparator, - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + comparator, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [flyoutMap]; @@ -160,10 +230,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapErrorCount = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapErrorCount]; @@ -171,10 +243,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapTransactionErrorRate = { observedValue: [asPercent(alert.fields[ALERT_EVALUATION_VALUE], 100)], threshold: [asPercent(alert.fields[ALERT_EVALUATION_THRESHOLD], 100)], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapTransactionErrorRate]; @@ -182,22 +256,26 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const APMFlyoutMapTransactionDuration = { observedValue: [asDuration(alert.fields[ALERT_EVALUATION_VALUE])], threshold: [asDuration(alert.fields[ALERT_EVALUATION_THRESHOLD])], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [APMFlyoutMapTransactionDuration]; case '.es-query': - const { thresholdComparator } = ruleParams as EsQueryRuleParams; + const { thresholdComparator, threshold } = ruleParams as EsQueryRuleParams; const ESQueryFlyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], - threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], + threshold: threshold.join(' AND '), comparator: thresholdComparator, - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + pctAboveThreshold: getPctAboveThreshold( + threshold, + thresholdComparator as COMPARATORS, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [ESQueryFlyoutMap]; @@ -205,10 +283,12 @@ export const mapRuleParamsWithFlyout = (alert: TopAlert): FlyoutThresholdData[] const SLOBurnRateFlyoutMap = { observedValue: [alert.fields[ALERT_EVALUATION_VALUE]], threshold: [alert.fields[ALERT_EVALUATION_THRESHOLD]], - comparator: '>', - pctAboveThreshold: getPctAboveThreshold(alert.fields[ALERT_EVALUATION_VALUE], [ - alert.fields[ALERT_EVALUATION_THRESHOLD]!, - ]), + comparator: COMPARATORS.GREATER_THAN, + pctAboveThreshold: getPctAboveThreshold( + [alert.fields[ALERT_EVALUATION_THRESHOLD]!], + COMPARATORS.GREATER_THAN, + alert.fields[ALERT_EVALUATION_VALUE] + ), } as unknown as FlyoutThresholdData; return [SLOBurnRateFlyoutMap]; default: diff --git a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx index 32820cb164309..a1581333294ea 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx @@ -13,6 +13,8 @@ import { AlertStatus } from '@kbn/rule-data-utils'; import moment from 'moment'; import React from 'react'; import { Tooltip as CaseTooltip } from '@kbn/cases-components'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { LEGACY_COMPARATORS } from '../../../common/utils/convert_legacy_outside_comparator'; import type { Group } from '../../../common/custom_threshold_rule/types'; import { NavigateToCaseView } from '../../hooks/use_case_view_navigation'; import { Groups } from '../custom_threshold/components/alert_details_app_section/groups'; @@ -124,11 +126,18 @@ export const overviewColumns: Array> = [ return (
{ruleCriteria.map((criteria, criticalIndex) => { - const threshold = criteria.threshold; - const comparator = criteria.comparator; + const { threshold, comparator } = criteria; + let formattedComparator = comparator.toUpperCase(); + if ( + comparator === COMPARATORS.NOT_BETWEEN || + comparator === LEGACY_COMPARATORS.OUTSIDE_RANGE + ) { + // No need for i18n as we are using the enum value, we only need a space. + formattedComparator = 'NOT BETWEEN'; + } return ( -

{`${comparator.toUpperCase()} ${threshold}`}

+

{`${formattedComparator} ${threshold}`}

); })} diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts index 0e5da33f67d6c..05a8c3eb67a35 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/alert_details_app_section/helpers/log_rate_analysis_query.test.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { Aggregators, Comparator } from '../../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../../../../common/custom_threshold_rule/types'; import { CustomThresholdRuleTypeParams } from '../../../types'; import { getLogRateAnalysisEQQuery } from './log_rate_analysis_query'; @@ -29,7 +30,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }; @@ -85,7 +86,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }, @@ -108,7 +109,7 @@ describe('buildEsQuery', () => { timeSize: 1, timeUnit: 'm', threshold: [90], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ], }, diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx index a8e64ac343a0f..6be6f983c7e3c 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { Color } from '../../../../../common/custom_threshold_rule/color_palette'; -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { shallow } from 'enzyme'; import React from 'react'; @@ -30,7 +30,7 @@ describe('ThresholdAnnotations', () => { const defaultProps = { threshold: [20, 30], sortedThresholds: [20, 30], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, color: Color.color0, id: 'testId', firstTimestamp: 123456789, @@ -54,7 +54,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for in between thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.BETWEEN }); + const wrapper = await setup({ comparator: COMPARATORS.BETWEEN }); const annotation = wrapper.find('[data-test-subj="between-rect"]'); const expectedValues = [ @@ -73,7 +73,7 @@ describe('ThresholdAnnotations', () => { }); it('should render an upper rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-lower-rect"]'); const expectedValues = [ @@ -92,7 +92,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a lower rectangular annotation for outside range thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.OUTSIDE_RANGE }); + const wrapper = await setup({ comparator: COMPARATORS.NOT_BETWEEN }); const annotation = wrapper.find('[data-test-subj="outside-range-upper-rect"]'); const expectedValues = [ @@ -111,7 +111,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for below thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.LT }); + const wrapper = await setup({ comparator: COMPARATORS.LESS_THAN }); const annotation = wrapper.find('[data-test-subj="below-rect"]'); const expectedValues = [ @@ -130,7 +130,7 @@ describe('ThresholdAnnotations', () => { }); it('should render a rectangular annotation for above thresholds', async () => { - const wrapper = await setup({ comparator: Comparator.GT }); + const wrapper = await setup({ comparator: COMPARATORS.GREATER_THAN }); const annotation = wrapper.find('[data-test-subj="above-rect"]'); const expectedValues = [ diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx index e911eba6ad6c8..763d43ae5eab3 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/criterion_preview_chart/threshold_annotations.tsx @@ -7,13 +7,13 @@ import { AnnotationDomainType, LineAnnotation, RectAnnotation } from '@elastic/charts'; import { first, last } from 'lodash'; import React from 'react'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Color, colorTransformer } from '../../../../../common/custom_threshold_rule/color_palette'; -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; interface ThresholdAnnotationsProps { threshold: number[]; sortedThresholds: number[]; - comparator: Comparator; + comparator: COMPARATORS; color: Color; id: string; firstTimestamp: number; @@ -34,8 +34,10 @@ export function ThresholdAnnotations({ domain, }: ThresholdAnnotationsProps) { if (!comparator || !threshold) return null; - const isAbove = [Comparator.GT, Comparator.GT_OR_EQ].includes(comparator); - const isBelow = [Comparator.LT, Comparator.LT_OR_EQ].includes(comparator); + const isAbove = [COMPARATORS.GREATER_THAN, COMPARATORS.GREATER_THAN_OR_EQUALS].includes( + comparator + ); + const isBelow = [COMPARATORS.LESS_THAN, COMPARATORS.LESS_THAN_OR_EQUALS].includes(comparator); return ( <> - {sortedThresholds.length === 2 && comparator === Comparator.BETWEEN ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.BETWEEN ? ( <> ) : null} - {sortedThresholds.length === 2 && comparator === Comparator.OUTSIDE_RANGE ? ( + {sortedThresholds.length === 2 && comparator === COMPARATORS.NOT_BETWEEN ? ( <> = { timeSize: 1, timeUnit: 'm' as TimeUnitChar, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, fields: [ { name: 'system.cpu.user.pct', normalizedType: 'number' }, @@ -128,7 +128,7 @@ CustomEquationEditorWithEquationErrors.args = { timeSize: 1, timeUnit: 'm' as TimeUnitChar, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, errors: { equation: diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx index 674ce1c579756..f471333f53661 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx @@ -8,8 +8,7 @@ import React from 'react'; import { ComponentMeta } from '@storybook/react'; import { LIGHT_THEME } from '@elastic/charts'; - -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Props, Threshold as Component } from './custom_threshold'; export default { @@ -31,7 +30,7 @@ export default { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: [90], title: 'Threshold breached', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx index fbb728b4b81aa..e0c83d31ddb4c 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.test.tsx @@ -10,13 +10,13 @@ import { LIGHT_THEME } from '@elastic/charts'; import { render } from '@testing-library/react'; import { Props, Threshold } from './custom_threshold'; import React from 'react'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { chartProps: { baseTheme: LIGHT_THEME }, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, id: 'componentId', threshold: [90], title: 'Threshold breached', @@ -43,7 +43,7 @@ describe('Threshold', () => { it('shows component for between', () => { const component = renderComponent({ - comparator: Comparator.BETWEEN, + comparator: COMPARATORS.BETWEEN, threshold: [90, 95], }); expect(component.queryByTestId('thresholdRule-90-95-93')).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx index 7e9fbfce9d682..a82c369c57737 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/custom_threshold.tsx @@ -10,7 +10,7 @@ import { Chart, Metric, Settings } from '@elastic/charts'; import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; import type { PartialTheme, Theme } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; export interface ChartProps { theme?: PartialTheme; @@ -19,7 +19,7 @@ export interface ChartProps { export interface Props { chartProps: ChartProps; - comparator: Comparator | string; + comparator: COMPARATORS | string; id: string; threshold: number[]; title: string; diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx index 2bdd1f2def081..b5ea26f2fb907 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.test.tsx @@ -9,9 +9,10 @@ import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import React from 'react'; import { act } from 'react-dom/test-utils'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { ExpressionRow } from './expression_row'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe('ExpressionRow', () => { async function setup(expression: MetricExpression) { @@ -57,7 +58,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a percentage for pct metrics', async () => { const expression: MetricExpression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -82,7 +83,7 @@ describe('ExpressionRow', () => { it('should display thresholds as a decimal for all other metrics', async () => { const expression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx index ffff1212ce05b..0d4fe7ecafbb8 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/expression_row.tsx @@ -18,35 +18,20 @@ import { i18n } from '@kbn/i18n'; import React, { useCallback, useMemo, useState, ReactElement } from 'react'; import { AggregationType, - builtInComparators, - COMPARATORS, IErrorObject, ThresholdExpression, } from '@kbn/triggers-actions-ui-plugin/public'; import { DataViewBase, DataViewFieldBase } from '@kbn/es-query'; import { debounce } from 'lodash'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { convertToBuiltInComparators } from '../../../../common/utils/convert_legacy_outside_comparator'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { MetricExpression } from '../types'; import { CustomEquationEditor } from './custom_equation'; import { CUSTOM_EQUATION, LABEL_HELP_MESSAGE, LABEL_LABEL } from '../i18n_strings'; import { decimalToPct, pctToDecimal } from '../helpers/corrected_percent_convert'; import { isPercent } from '../helpers/threshold_unit'; -// Create a new object with COMPARATORS.NOT_BETWEEN removed as we use OUTSIDE_RANGE -const updatedBuiltInComparators = { ...builtInComparators }; -delete updatedBuiltInComparators[COMPARATORS.NOT_BETWEEN]; - -const customComparators = { - ...updatedBuiltInComparators, - [Comparator.OUTSIDE_RANGE]: { - text: i18n.translate('xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel', { - defaultMessage: 'Is not between', - }), - value: Comparator.OUTSIDE_RANGE, - requiredValues: 2, - }, -}; - interface ExpressionRowProps { title: ReactElement; fields: DataViewFieldBase[]; @@ -76,14 +61,13 @@ export const ExpressionRow: React.FC = (props) => { title, } = props; - const { metrics, comparator = Comparator.GT, threshold = [] } = expression; - + const { metrics, comparator = COMPARATORS.GREATER_THAN, threshold = [] } = expression; const isMetricPct = useMemo(() => isPercent(metrics), [metrics]); const [label, setLabel] = useState(expression?.label || undefined); const updateComparator = useCallback( (c?: string) => { - setRuleParams(expressionId, { ...expression, comparator: c as Comparator }); + setRuleParams(expressionId, { ...expression, comparator: c as COMPARATORS }); }, [expressionId, expression, setRuleParams] ); @@ -214,12 +198,17 @@ const ThresholdElement: React.FC<{ return threshold; }, [threshold, isMetricPct]); + const thresholdComparator = useCallback(() => { + if (!comparator) return COMPARATORS.GREATER_THAN; + // Check if the rule had a legacy OUTSIDE_RANGE inside its params. + // Then, change it on-the-fly to NOT_BETWEEN + return convertToBuiltInComparators(comparator); + }, [comparator]); return ( <> { timeUnit: 'm', sourceId: 'default', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, }; const { wrapper } = await setup(expression); expect(wrapper.find('[data-test-subj="thresholdRuleNoChartData"]').exists()).toBeTruthy(); diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx index 08de905213f87..1e326e3fa5f6d 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/rule_condition_chart/rule_condition_chart.tsx @@ -25,11 +25,9 @@ import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; import { TimeRange } from '@kbn/es-query'; import { EventAnnotationConfig } from '@kbn/event-annotation-common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { EventsAsUnit } from '../../../../../common/constants'; -import { - Comparator, - CustomThresholdSearchSourceFields, -} from '../../../../../common/custom_threshold_rule/types'; +import { CustomThresholdSearchSourceFields } from '../../../../../common/custom_threshold_rule/types'; import { useKibana } from '../../../../utils/kibana_react'; import { MetricExpression } from '../../types'; import { AggMap, PainlessTinyMathParser } from './painless_tinymath_parser'; @@ -121,15 +119,15 @@ export function RuleConditionChart({ const refLayers = []; if ( - comparator === Comparator.OUTSIDE_RANGE || - (comparator === Comparator.BETWEEN && threshold.length === 2) + comparator === COMPARATORS.NOT_BETWEEN || + (comparator === COMPARATORS.BETWEEN && threshold.length === 2) ) { const refLineStart = new XYReferenceLinesLayer({ data: [ { value: (threshold[0] || 0).toString(), color: euiTheme.colors.danger, - fill: comparator === Comparator.OUTSIDE_RANGE ? 'below' : 'none', + fill: comparator === COMPARATORS.NOT_BETWEEN ? 'below' : 'none', }, ], }); @@ -138,7 +136,7 @@ export function RuleConditionChart({ { value: (threshold[1] || 0).toString(), color: euiTheme.colors.danger, - fill: comparator === Comparator.OUTSIDE_RANGE ? 'above' : 'none', + fill: comparator === COMPARATORS.NOT_BETWEEN ? 'above' : 'none', }, ], }); @@ -146,7 +144,7 @@ export function RuleConditionChart({ refLayers.push(refLineStart, refLineEnd); } else { let fill: FillStyle = 'above'; - if (comparator === Comparator.LT || comparator === Comparator.LT_OR_EQ) { + if (comparator === COMPARATORS.LESS_THAN || comparator === COMPARATORS.LESS_THAN_OR_EQUALS) { fill = 'below'; } const thresholdRefLine = new XYReferenceLinesLayer({ diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx index 87cefa2742bbb..184187c705238 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/components/validation.tsx @@ -11,8 +11,8 @@ import { buildEsQuery, fromKueryExpression } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { isEmpty } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { - Comparator, CustomMetricExpressionParams, CustomThresholdSearchSourceFields, } from '../../../../common/custom_threshold_rule/types'; @@ -111,7 +111,7 @@ export function validateCustomThreshold({ // The Threshold component returns an empty array with a length ([empty]) because it's using delete newThreshold[i]. // We need to use [...c.threshold] to convert it to an array with an undefined value ([undefined]) so we can test each element. const { comparator, threshold } = { comparator: c.comparator, threshold: c.threshold } as { - comparator?: Comparator; + comparator?: COMPARATORS; threshold?: number[]; }; if (threshold && threshold.length && ![...threshold].every(isNumber)) { @@ -130,7 +130,7 @@ export function validateCustomThreshold({ }); } - if (comparator === Comparator.BETWEEN && (!threshold || threshold.length < 2)) { + if (comparator === COMPARATORS.BETWEEN && (!threshold || threshold.length < 2)) { errors[id].critical.threshold1.push( i18n.translate( 'xpack.observability.customThreshold.rule.alertFlyout.error.thresholdRequired', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index 8f569e1d80b17..02c428ccf3698 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -13,7 +13,8 @@ import { queryClient } from '@kbn/osquery-plugin/public/query_client'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; import { QueryClientProvider } from '@tanstack/react-query'; import { act } from 'react-dom/test-utils'; -import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; +import { Aggregators } from '../../../common/custom_threshold_rule/types'; import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import Expressions from './custom_threshold_rule_expression'; @@ -152,7 +153,7 @@ describe('Expression', () => { aggType: Aggregators.COUNT, }, ], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [100], timeSize: 1, timeUnit: 'm', @@ -178,7 +179,7 @@ describe('Expression', () => { { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, ], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, equation: 'A * B', label: 'prefill label', threshold: [500], @@ -200,7 +201,7 @@ describe('Expression', () => { { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, ], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, equation: 'A * B', label: 'prefill label', threshold: [500], diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 41127b2880233..f3aff841233cf 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -36,8 +36,9 @@ import { RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { useKibana } from '../../utils/kibana_react'; -import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../common/custom_threshold_rule/types'; import { TimeUnitChar } from '../../../common/utils/formatters/duration'; import { AlertContextMeta, AlertParams, MetricExpression } from './types'; import { ExpressionRow } from './components/expression_row'; @@ -53,7 +54,7 @@ type Props = Omit< >; export const defaultExpression: MetricExpression = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', diff --git a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts index 045c04219801e..efa32473e0b44 100644 --- a/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts +++ b/x-pack/plugins/observability_solution/observability/public/components/custom_threshold/mocks/custom_threshold_rule.ts @@ -7,8 +7,9 @@ import { v4 as uuidv4 } from 'uuid'; import { ParsedTechnicalFields } from '@kbn/rule-registry-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { CustomThresholdAlertFields } from '../types'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { CustomThresholdAlert, CustomThresholdRule } from '../components/types'; @@ -60,7 +61,7 @@ export const buildCustomThresholdRule = ( params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -72,7 +73,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'B', @@ -83,11 +84,9 @@ export const buildCustomThresholdRule = ( threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: Comparator.GT, - warningThreshold: [2.2], }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'C', @@ -100,7 +99,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -115,7 +114,7 @@ export const buildCustomThresholdRule = ( 'A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A + A', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'C', @@ -133,7 +132,7 @@ export const buildCustomThresholdRule = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'CAD', @@ -209,7 +208,7 @@ export const buildCustomThresholdAlert = ( 'kibana.alert.rule.parameters': { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'A', @@ -222,7 +221,7 @@ export const buildCustomThresholdAlert = ( timeUnit: 'm', }, { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { name: 'B', @@ -233,7 +232,7 @@ export const buildCustomThresholdAlert = ( threshold: [4], timeSize: 15, timeUnit: 'm', - warningComparator: Comparator.GT, + warningComparator: COMPARATORS.GREATER_THAN, warningThreshold: [2.2], }, ], diff --git a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx index bdaf10b04ca69..31ae9e41d0529 100644 --- a/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx +++ b/x-pack/plugins/observability_solution/observability/public/pages/rule_details/rule_details.tsx @@ -194,7 +194,6 @@ export function RuleDetailsPage() { : ''; if (isLoading || isRuleDeleting) return ; - if (!rule || isError) return ; return ( diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts index 8f65682c68fb1..cb99f17a9db5c 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.test.ts @@ -13,9 +13,9 @@ import { FIRED_ACTION, NO_DATA_ACTION } from './constants'; import { Evaluation } from './lib/evaluate_rule'; import type { LogMeta, Logger } from '@kbn/logging'; import { DEFAULT_FLAPPING_SETTINGS } from '@kbn/alerting-plugin/common'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, CustomThresholdExpressionMetric, } from '../../../../common/custom_threshold_rule/types'; @@ -234,7 +234,7 @@ describe('The custom threshold alert type', () => { beforeEach(() => jest.clearAllMocks()); afterAll(() => clearInstances()); const instanceID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -251,7 +251,7 @@ describe('The custom threshold alert type', () => { }, }); const setResults = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], shouldFire: boolean = false, isNoData: boolean = false @@ -271,62 +271,62 @@ describe('The custom threshold alert type', () => { }, ]); test('alerts as expected with the > comparator', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT, [1.5], false); - await execute(Comparator.GT, [1.5]); + setResults(COMPARATORS.GREATER_THAN, [1.5], false); + await execute(COMPARATORS.GREATER_THAN, [1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the < comparator', async () => { - setResults(Comparator.LT, [1.5], true); - await execute(Comparator.LT, [1.5]); + setResults(COMPARATORS.LESS_THAN, [1.5], true); + await execute(COMPARATORS.LESS_THAN, [1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT, [0.75], false); - await execute(Comparator.LT, [0.75]); + setResults(COMPARATORS.LESS_THAN, [0.75], false); + await execute(COMPARATORS.LESS_THAN, [0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the >= comparator', async () => { - setResults(Comparator.GT_OR_EQ, [0.75], true); - await execute(Comparator.GT_OR_EQ, [0.75]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT_OR_EQ, [1.0], true); - await execute(Comparator.GT_OR_EQ, [1.0]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], true); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.GT_OR_EQ, [1.5], false); - await execute(Comparator.GT_OR_EQ, [1.5]); + setResults(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5], false); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the <= comparator', async () => { - setResults(Comparator.LT_OR_EQ, [1.5], true); - await execute(Comparator.LT_OR_EQ, [1.5]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT_OR_EQ, [1.0], true); - await execute(Comparator.LT_OR_EQ, [1.0]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], true); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.LT_OR_EQ, [0.75], false); - await execute(Comparator.LT_OR_EQ, [0.75]); + setResults(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75], false); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts as expected with the between comparator', async () => { - setResults(Comparator.BETWEEN, [0, 1.5], true); - await execute(Comparator.BETWEEN, [0, 1.5]); + setResults(COMPARATORS.BETWEEN, [0, 1.5], true); + await execute(COMPARATORS.BETWEEN, [0, 1.5]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.BETWEEN, [0, 0.75], false); - await execute(Comparator.BETWEEN, [0, 0.75]); + setResults(COMPARATORS.BETWEEN, [0, 0.75], false); + await execute(COMPARATORS.BETWEEN, [0, 0.75]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); - test('alerts as expected with the outside range comparator', async () => { - setResults(Comparator.OUTSIDE_RANGE, [0, 0.75], true); - await execute(Comparator.OUTSIDE_RANGE, [0, 0.75]); + test('alerts as expected with the not between comparator', async () => { + setResults(COMPARATORS.NOT_BETWEEN, [0, 0.75], true); + await execute(COMPARATORS.NOT_BETWEEN, [0, 0.75]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); - setResults(Comparator.OUTSIDE_RANGE, [0, 1.5], false); - await execute(Comparator.OUTSIDE_RANGE, [0, 1.5]); + setResults(COMPARATORS.NOT_BETWEEN, [0, 1.5], false); + await execute(COMPARATORS.NOT_BETWEEN, [0, 1.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('reports expected values to the action context', async () => { - setResults(Comparator.GT, [0.75], true); - await execute(Comparator.GT, [0.75]); + setResults(COMPARATORS.GREATER_THAN, [0.75], true); + await execute(COMPARATORS.GREATER_THAN, [0.75]); const reportedAlert = getLastReportedAlert(instanceID); expect(reportedAlert?.context?.group).toBeUndefined(); expect(reportedAlert?.context?.reason).toBe( @@ -339,7 +339,7 @@ describe('The custom threshold alert type', () => { beforeEach(() => jest.clearAllMocks()); afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['groupByField'], metrics?: CustomThresholdExpressionMetric[], @@ -369,7 +369,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -379,7 +379,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -389,7 +389,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toHaveAlertAction(); }); @@ -398,7 +398,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -408,7 +408,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1.5], currentValue: 3, timestamp: new Date().toISOString(), @@ -418,7 +418,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.LT, [1.5]); + await execute(COMPARATORS.LESS_THAN, [1.5]); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -427,7 +427,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -437,7 +437,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [5], currentValue: 3, timestamp: new Date().toISOString(), @@ -447,7 +447,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [5]); + await execute(COMPARATORS.GREATER_THAN, [5]); expect(getLastReportedAlert(instanceIdA)).toBe(undefined); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -456,7 +456,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -466,7 +466,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -476,7 +476,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)?.context?.group).toEqual([ { field: 'groupByField', value: 'a' }, ]); @@ -489,7 +489,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -506,7 +506,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -523,7 +523,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -541,7 +541,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField'], [ @@ -557,7 +557,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -567,7 +567,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -577,7 +577,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -588,7 +588,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField'], [ @@ -607,7 +607,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -617,7 +617,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -628,7 +628,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult3 } = await execute( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], ['groupByField', 'groupByField-else'], [ @@ -644,7 +644,7 @@ describe('The custom threshold alert type', () => { }); const executeWithFilter = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], filterQuery: string, metrics?: any, @@ -679,7 +679,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -696,7 +696,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -713,7 +713,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -731,7 +731,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -741,7 +741,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -751,7 +751,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -761,7 +761,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -772,7 +772,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -785,7 +785,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -795,7 +795,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -806,7 +806,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'different' }), [ @@ -825,7 +825,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -842,7 +842,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -859,7 +859,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], metrics: [ { @@ -877,7 +877,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult1 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.2' @@ -887,7 +887,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -897,7 +897,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -907,7 +907,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -918,7 +918,7 @@ describe('The custom threshold alert type', () => { }, ]); const { state: stateResult2 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -934,7 +934,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -944,7 +944,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: null, timestamp: new Date().toISOString(), @@ -957,7 +957,7 @@ describe('The custom threshold alert type', () => { // Consider c as untracked services.alertsClient.isTrackedAlert.mockImplementation((id: string) => id !== 'c'); const { state: stateResult3 } = await executeWithFilter( - Comparator.GT, + COMPARATORS.GREATER_THAN, [0.75], JSON.stringify({ query: 'q' }), 'test.metric.1', @@ -973,7 +973,7 @@ describe('The custom threshold alert type', () => { describe('querying with a groupBy parameter host.name and rule tags', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string[] = ['host.name'], metrics?: any, @@ -1008,7 +1008,7 @@ describe('The custom threshold alert type', () => { { 'host-01': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1021,7 +1021,7 @@ describe('The custom threshold alert type', () => { }, 'host-02': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3, timestamp: new Date().toISOString(), @@ -1034,7 +1034,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceIdA)?.context?.tags).toStrictEqual([ 'host-01_tag1', 'host-01_tag2', @@ -1053,7 +1053,7 @@ describe('The custom threshold alert type', () => { describe('querying without a groupBy parameter and rule tags', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], groupBy: string = '', metrics?: any, @@ -1086,7 +1086,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1098,7 +1098,7 @@ describe('The custom threshold alert type', () => { ]); const instanceID = '*'; - await execute(Comparator.GT, [0.75]); + await execute(COMPARATORS.GREATER_THAN, [0.75]); expect(getLastReportedAlert(instanceID)?.context?.tags).toStrictEqual([ 'ruleTag1', 'ruleTag2', @@ -1109,7 +1109,7 @@ describe('The custom threshold alert type', () => { describe('querying with multiple criteria', () => { afterAll(() => clearInstances()); const execute = ( - comparator: Comparator, + comparator: COMPARATORS, thresholdA: number[], thresholdB: number[], groupBy: string = '', @@ -1148,7 +1148,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1160,7 +1160,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], currentValue: 3.0, timestamp: new Date().toISOString(), @@ -1171,7 +1171,7 @@ describe('The custom threshold alert type', () => { }, ]); const instanceID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); }); test('sends no alert when some, but not all, criteria cross the threshold', async () => { @@ -1179,7 +1179,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1191,7 +1191,7 @@ describe('The custom threshold alert type', () => { {}, ]); const instanceID = '*'; - await execute(Comparator.LT_OR_EQ, [1.0], [2.5]); + await execute(COMPARATORS.LESS_THAN_OR_EQUALS, [1.0], [2.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); test('alerts only on groups that meet all criteria when querying with a groupBy parameter', async () => { @@ -1199,7 +1199,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1209,7 +1209,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 3.0, timestamp: new Date().toISOString(), @@ -1221,7 +1221,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1238,7 +1238,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1257,7 +1257,7 @@ describe('The custom threshold alert type', () => { ]); const instanceIdA = 'a'; const instanceIdB = 'b'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0], 'groupByField'); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0], 'groupByField'); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); }); @@ -1266,7 +1266,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [1.0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1278,7 +1278,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [3.0], metrics: [ { @@ -1296,7 +1296,7 @@ describe('The custom threshold alert type', () => { }, ]); const instanceID = '*'; - await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + await execute(COMPARATORS.GREATER_THAN_OR_EQUALS, [1.0], [3.0]); const reportedAlert = getLastReportedAlert(instanceID); const reasons = reportedAlert?.context?.reason; expect(reasons).toBe( @@ -1308,7 +1308,7 @@ describe('The custom threshold alert type', () => { describe('querying with the count aggregator', () => { afterAll(() => clearInstances()); const instanceID = '*'; - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1329,7 +1329,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], currentValue: 1, timestamp: new Date().toISOString(), @@ -1339,13 +1339,13 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); expect(getLastReportedAlert(instanceID)).toHaveAlertAction(); setEvaluationResults([ { '*': { ...customThresholdCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [0.5], currentValue: 1, timestamp: new Date().toISOString(), @@ -1355,12 +1355,12 @@ describe('The custom threshold alert type', () => { }, }, ]); - await execute(Comparator.LT, [0.5]); + await execute(COMPARATORS.LESS_THAN, [0.5]); expect(getLastReportedAlert(instanceID)).toBe(undefined); }); describe('with a groupBy parameter', () => { const executeGroupBy = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], sourceId: string = 'default', state?: any @@ -1390,7 +1390,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1400,7 +1400,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1410,14 +1410,14 @@ describe('The custom threshold alert type', () => { }, }, ]); - const resultState = await executeGroupBy(Comparator.LT_OR_EQ, [0]); + const resultState = await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0]); expect(getLastReportedAlert(instanceIdA)).toBe(undefined); expect(getLastReportedAlert(instanceIdB)).toBe(undefined); setEvaluationResults([ { a: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 0, timestamp: new Date().toISOString(), @@ -1427,7 +1427,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdCountCriterion, - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, threshold: [0], currentValue: 0, timestamp: new Date().toISOString(), @@ -1437,7 +1437,7 @@ describe('The custom threshold alert type', () => { }, }, ]); - await executeGroupBy(Comparator.LT_OR_EQ, [0], 'empty-response', resultState); + await executeGroupBy(COMPARATORS.LESS_THAN_OR_EQUALS, [0], 'empty-response', resultState); expect(getLastReportedAlert(instanceIdA)).toHaveAlertAction(); expect(getLastReportedAlert(instanceIdB)).toHaveAlertAction(); }); @@ -1446,7 +1446,7 @@ describe('The custom threshold alert type', () => { describe('querying recovered alert with a count aggregator', () => { afterAll(() => clearInstances()); - const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => + const execute = (comparator: COMPARATORS, threshold: number[], sourceId: string = 'default') => executor({ ...mockOptions, services, @@ -1467,7 +1467,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], currentValue: 1, timestamp: new Date().toISOString(), @@ -1488,7 +1488,7 @@ describe('The custom threshold alert type', () => { ]), }; }); - await execute(Comparator.GT, [0.9]); + await execute(COMPARATORS.GREATER_THAN, [0.9]); const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; expect(getViewInAppUrl).toBeCalledWith({ dataViewId: 'c34a7c79-a88b-4b4a-ad19-72f6d24104e4', @@ -1519,7 +1519,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metrics: [ { @@ -1538,7 +1538,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1567,7 +1567,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1602,7 +1602,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [1], metrics: [ { @@ -1614,7 +1614,7 @@ describe('The custom threshold alert type', () => { }, { ...customThresholdCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [30], }, ], @@ -1626,7 +1626,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, threshold: [1], metrics: [ { @@ -1675,7 +1675,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics, }, @@ -1700,7 +1700,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1723,7 +1723,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1746,7 +1746,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -1756,7 +1756,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -1780,7 +1780,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1797,7 +1797,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1824,7 +1824,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1841,7 +1841,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1858,7 +1858,7 @@ describe('The custom threshold alert type', () => { }, c: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1884,7 +1884,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -1894,7 +1894,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -1923,7 +1923,7 @@ describe('The custom threshold alert type', () => { criteria: [ { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics, }, @@ -1944,7 +1944,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1967,7 +1967,7 @@ describe('The custom threshold alert type', () => { { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -1990,7 +1990,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 1, timestamp: new Date().toISOString(), @@ -2000,7 +2000,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], currentValue: 3, timestamp: new Date().toISOString(), @@ -2022,7 +2022,7 @@ describe('The custom threshold alert type', () => { { a: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -2039,7 +2039,7 @@ describe('The custom threshold alert type', () => { }, b: { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0], metrics: [ { @@ -2116,7 +2116,7 @@ declare global { } const customThresholdNonCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.AVERAGE, @@ -2131,7 +2131,7 @@ const customThresholdNonCountCriterion: CustomMetricExpressionParams = { const mockedCountFilter = 'mockedCountFilter'; const customThresholdCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.COUNT, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts index 21ee4358b595a..94be15415abb8 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts @@ -33,11 +33,7 @@ import { CustomThresholdActionGroup, CustomThresholdAlert, } from './types'; -import { - buildFiredAlertReason, - buildNoDataAlertReason, - // buildRecoveredAlertReason, -} from './messages'; +import { buildFiredAlertReason, buildNoDataAlertReason } from './messages'; import { createScopedLogger, hasAdditionalContext, @@ -91,6 +87,7 @@ export const createCustomThresholdExecutor = ({ } = options; const { criteria } = params; + if (criteria.length === 0) throw new Error('Cannot execute an alert with 0 conditions'); const thresholdLogger = createScopedLogger(logger, 'thresholdRule', { alertId: ruleId, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts index 9a1dde442983c..48d355ee4ce33 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_bucket_selector.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { convertToBuiltInComparators } from '../../../../../common'; import { CustomMetricExpressionParams } from '../../../../../common/custom_threshold_rule/types'; import { createConditionScript } from './create_condition_script'; import { createLastPeriod } from './wrap_in_period'; @@ -24,7 +25,10 @@ export const createBucketSelector = ( buckets_path: { value: bucketPath, }, - script: createConditionScript(condition.threshold, condition.comparator), + script: createConditionScript( + condition.threshold, + convertToBuiltInComparators(condition.comparator) + ), }, }; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts index 2e5eda9fa32b4..48a105d3700be 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/create_condition_script.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { Comparator } from '../../../../../common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; -export const createConditionScript = (threshold: number[], comparator: Comparator) => { - if (comparator === Comparator.BETWEEN && threshold.length === 2) { +export const createConditionScript = (threshold: number[], comparator: COMPARATORS) => { + if (comparator === COMPARATORS.BETWEEN && threshold.length === 2) { return { source: `params.value > params.threshold0 && params.value < params.threshold1 ? 1 : 0`, params: { @@ -17,9 +17,9 @@ export const createConditionScript = (threshold: number[], comparator: Comparato }, }; } - if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) { + if (comparator === COMPARATORS.NOT_BETWEEN && threshold.length === 2) { return { - // OUTSIDE_RANGE/NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 + // NOT BETWEEN is the opposite of BETWEEN. Use the BETWEEN condition and switch the 1 and 0 source: `params.value > params.threshold0 && params.value < params.threshold1 ? 0 : 1`, params: { threshold0: threshold[0], diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts index ac55b7f76f383..c3c0d8b2eee94 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/get_data.ts @@ -194,7 +194,6 @@ export const getData = async ( const fieldsExisted = groupBy?.includes(KUBERNETES_POD_UID) ? await doFieldsExist(esClient, [CONTAINER_ID], index) : null; - const request = { index, allow_no_indices: true, diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts index b63e72e19fd29..e10a4f4cae9be 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/lib/metric_query.test.ts @@ -7,12 +7,12 @@ import moment from 'moment'; import { - Comparator, Aggregators, CustomMetricExpressionParams, SearchConfigurationType, } from '../../../../../common/custom_threshold_rule/types'; import { getElasticsearchMetricQuery } from './metric_query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { const expressionParams: CustomMetricExpressionParams = { @@ -26,7 +26,7 @@ describe("The Metric Threshold Alert's getElasticsearchMetricQuery", () => { timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const searchConfiguration: SearchConfigurationType = { index: { diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts index ca160d06b6573..046b7b5879026 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/messages.ts @@ -6,58 +6,58 @@ */ import { i18n } from '@kbn/i18n'; -import { Comparator } from '../../../../common/custom_threshold_rule/types'; -import { formatDurationFromTimeUnitChar } from '../../../../common'; -import { Evaluation } from './lib/evaluate_rule'; -import { formatAlertResult, FormattedEvaluation } from './lib/format_alert_result'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { - BELOW_TEXT, ABOVE_TEXT, - BETWEEN_TEXT, - NOT_BETWEEN_TEXT, - CUSTOM_EQUATION_I18N, ABOVE_OR_EQ_TEXT, + BELOW_TEXT, BELOW_OR_EQ_TEXT, -} from './translations'; + BETWEEN_TEXT, + NOT_BETWEEN_TEXT, +} from '../../../../common/i18n'; +import { convertToBuiltInComparators, formatDurationFromTimeUnitChar } from '../../../../common'; +import { Evaluation } from './lib/evaluate_rule'; +import { formatAlertResult, FormattedEvaluation } from './lib/format_alert_result'; +import { CUSTOM_EQUATION_I18N } from './translations'; import { UNGROUPED_FACTORY_KEY } from './constants'; const toNumber = (value: number | string) => typeof value === 'string' ? parseFloat(value) : value; const recoveredComparatorToI18n = ( - comparator: Comparator, + comparator: COMPARATORS, threshold: number[], currentValue: number ) => { switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return currentValue < threshold[0] ? BELOW_TEXT : ABOVE_TEXT; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return BETWEEN_TEXT; - case Comparator.GT: + case COMPARATORS.GREATER_THAN: return ABOVE_TEXT; - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return ABOVE_OR_EQ_TEXT; - case Comparator.LT: + case COMPARATORS.LESS_THAN: return BELOW_TEXT; - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN_OR_EQUALS: return BELOW_OR_EQ_TEXT; } }; -const alertComparatorToI18n = (comparator: Comparator) => { +const alertComparatorToI18n = (comparator: COMPARATORS) => { switch (comparator) { - case Comparator.BETWEEN: + case COMPARATORS.BETWEEN: return BETWEEN_TEXT; - case Comparator.OUTSIDE_RANGE: + case COMPARATORS.NOT_BETWEEN: return NOT_BETWEEN_TEXT; - case Comparator.GT: + case COMPARATORS.GREATER_THAN: return ABOVE_TEXT; - case Comparator.GT_OR_EQ: + case COMPARATORS.GREATER_THAN_OR_EQUALS: return ABOVE_OR_EQ_TEXT; - case Comparator.LT: + case COMPARATORS.LESS_THAN: return BELOW_TEXT; - case Comparator.LT_OR_EQ: + case COMPARATORS.LESS_THAN_OR_EQUALS: return BELOW_OR_EQ_TEXT; } }; @@ -124,7 +124,7 @@ const buildAggregationReason: (evaluation: FormattedEvaluation) => string = ({ defaultMessage: '{label} is {currentValue}, {comparator} the threshold of {threshold}', values: { label, - comparator: alertComparatorToI18n(comparator), + comparator: alertComparatorToI18n(convertToBuiltInComparators(comparator)), threshold: thresholdToI18n(threshold), currentValue, }, @@ -134,7 +134,7 @@ const buildAggregationReason: (evaluation: FormattedEvaluation) => string = ({ export const buildRecoveredAlertReason: (alertResult: { group: string; label?: string; - comparator: Comparator; + comparator: COMPARATORS; threshold: Array; currentValue: number | string; }) => string = ({ group, label = CUSTOM_EQUATION_I18N, comparator, threshold, currentValue }) => diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts index b8ab169b7a90d..08c6e60298939 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_alert_result.ts @@ -5,15 +5,15 @@ * 2.0. */ +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, } from '../../../../../common/custom_threshold_rule/types'; import { Evaluation } from '../lib/evaluate_rule'; const customThresholdNonCountCriterion: CustomMetricExpressionParams = { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, metrics: [ { aggType: Aggregators.AVERAGE, @@ -30,7 +30,7 @@ export const alertResultsMultipleConditions: Array> = { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 1.0, timestamp: new Date().toISOString(), @@ -42,7 +42,7 @@ export const alertResultsMultipleConditions: Array> = { '*': { ...customThresholdNonCountCriterion, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.75], currentValue: 3.0, timestamp: new Date().toISOString(), diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts index 6c3eb7557302c..09e202fcac7d9 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/mocks/custom_threshold_metric_params.ts @@ -5,9 +5,9 @@ * 2.0. */ +import { COMPARATORS } from '@kbn/alerting-comparators'; import { Aggregators, - Comparator, CustomMetricExpressionParams, } from '../../../../../common/custom_threshold_rule/types'; @@ -28,7 +28,7 @@ export const criteriaMultipleConditions: CustomMetricExpressionParams[] = [ timeUnit: 'm', timeSize: 1, threshold: [1], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, { metrics: [ @@ -46,7 +46,7 @@ export const criteriaMultipleConditions: CustomMetricExpressionParams[] = [ timeUnit: 'm', timeSize: 1, threshold: [4], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ]; @@ -67,7 +67,7 @@ export const criteriaMultipleConditionsWithIsBetween: CustomMetricExpressionPara timeUnit: 'm', timeSize: 1, threshold: [1, 2], - comparator: Comparator.BETWEEN, + comparator: COMPARATORS.BETWEEN, }, { metrics: [ @@ -85,6 +85,6 @@ export const criteriaMultipleConditionsWithIsBetween: CustomMetricExpressionPara timeUnit: 'm', timeSize: 1, threshold: [4], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }, ]; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts index 8d3a968d85a64..f198a6c707968 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/register_custom_threshold_rule_type.ts @@ -15,9 +15,11 @@ import { IBasePath, Logger } from '@kbn/core/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { LicenseType } from '@kbn/licensing-plugin/server'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { EsQueryRuleParamsExtractedParams } from '@kbn/stack-alerts-plugin/server/rule_types/es_query/rule_type_params'; +import { LEGACY_COMPARATORS } from '../../../../common/utils/convert_legacy_outside_comparator'; import { observabilityFeatureId, observabilityPaths } from '../../../../common'; -import { Aggregators, Comparator } from '../../../../common/custom_threshold_rule/types'; +import { Aggregators } from '../../../../common/custom_threshold_rule/types'; import { THRESHOLD_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; import { @@ -75,9 +77,10 @@ export function thresholdRuleType( logger: Logger, locators: CustomThresholdLocators ) { + const comparators = Object.values({ ...COMPARATORS, ...LEGACY_COMPARATORS }); const baseCriterion = { threshold: schema.arrayOf(schema.number()), - comparator: oneOfLiterals(Object.values(Comparator)), + comparator: oneOfLiterals(comparators), timeUnit: schema.string(), timeSize: schema.number(), }; diff --git a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts index 020229c27a4ad..ebae07d124ebb 100644 --- a/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts +++ b/x-pack/plugins/observability_solution/observability/server/lib/rules/custom_threshold/translations.ts @@ -85,50 +85,6 @@ export const CUSTOM_EQUATION_I18N = i18n.translate( } ); -// Comparators - -export const BELOW_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.below', - { - defaultMessage: 'below', - } -); - -export const BELOW_OR_EQ_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.belowOrEqual', - { - defaultMessage: 'below or equal', - } -); - -export const ABOVE_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.above', - { - defaultMessage: 'above', - } -); - -export const ABOVE_OR_EQ_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.aboveOrEqual', - { - defaultMessage: 'above or equal', - } -); - -export const BETWEEN_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.between', - { - defaultMessage: 'between', - } -); - -export const NOT_BETWEEN_TEXT = i18n.translate( - 'xpack.observability.customThreshold.rule.threshold.notBetween', - { - defaultMessage: 'not between', - } -); - // Action variable descriptions export const groupByKeysActionVariableDescription = i18n.translate( diff --git a/x-pack/plugins/observability_solution/observability/tsconfig.json b/x-pack/plugins/observability_solution/observability/tsconfig.json index b4f617079dc58..5a0ddfd8678c0 100644 --- a/x-pack/plugins/observability_solution/observability/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability/tsconfig.json @@ -98,6 +98,7 @@ "@kbn/data-view-field-editor-plugin", "@kbn/cases-components", "@kbn/aiops-log-rate-analysis", + "@kbn/alerting-comparators", "@kbn/react-kibana-context-render", "@kbn/react-kibana-mount", ], diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts index 42d63dced13fd..e2571795a6e3b 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/scripts/evaluation/alert_templates/templates.ts @@ -5,14 +5,7 @@ * 2.0. */ -export enum Comparator { - GT = '>', - LT = '<', - GT_OR_EQ = '>=', - LT_OR_EQ = '<=', - BETWEEN = 'between', - OUTSIDE_RANGE = 'outside', -} +import { COMPARATORS } from '@kbn/alerting-comparators'; export enum Aggregators { COUNT = 'count', @@ -49,7 +42,7 @@ export const customThresholdAIAssistantLogCount = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [10], timeSize: 2, timeUnit: 'h', @@ -82,7 +75,7 @@ export const customThresholdAIAssistantMetricAvg = { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 2, timeUnit: 'h', diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json index 90c4f4d415142..ce98c24be2a25 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json +++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/tsconfig.json @@ -68,7 +68,8 @@ "@kbn/serverless", "@kbn/task-manager-plugin", "@kbn/cloud-plugin", - "@kbn/observability-plugin" + "@kbn/observability-plugin", + "@kbn/alerting-comparators" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/rule_registry/tsconfig.json b/x-pack/plugins/rule_registry/tsconfig.json index 385c3fe82e0f8..879d2eb9536cc 100644 --- a/x-pack/plugins/rule_registry/tsconfig.json +++ b/x-pack/plugins/rule_registry/tsconfig.json @@ -31,11 +31,11 @@ "@kbn/logging", "@kbn/securitysolution-io-ts-utils", "@kbn/share-plugin", - "@kbn/alerting-state-types", "@kbn/alerts-as-data-utils", "@kbn/core-http-router-server-mocks", "@kbn/core-http-server", "@kbn/search-types", + "@kbn/alerting-state-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts index 5c3c811143042..7d01043bfbc21 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/constants.ts @@ -5,8 +5,7 @@ * 2.0. */ -import { COMPARATORS } from '@kbn/triggers-actions-ui-plugin/public'; - +import { COMPARATORS } from '@kbn/alerting-comparators'; export const DEFAULT_VALUES = { THRESHOLD_COMPARATOR: COMPARATORS.GREATER_THAN, QUERY: `{ diff --git a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts index d987c5c6377a8..c8119110d76a2 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts +++ b/x-pack/plugins/stack_alerts/public/rule_types/es_query/validation.ts @@ -12,8 +12,8 @@ import { builtInComparators, builtInAggregationTypes, builtInGroupByTypes, - COMPARATORS, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { MAX_SELECTABLE_SOURCE_FIELDS, MAX_SELECTABLE_GROUP_BY_TERMS, diff --git a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx index 515517577251f..de0091a8202c7 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/threshold/expression.tsx @@ -20,7 +20,6 @@ import { HttpSetup } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { getFields, - COMPARATORS, builtInComparators, OfExpression, ThresholdExpression, @@ -30,6 +29,7 @@ import { builtInAggregationTypes, RuleTypeParamsExpressionProps, } from '@kbn/triggers-actions-ui-plugin/public'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { ThresholdVisualization } from './visualization'; import { IndexThresholdRuleParams } from './types'; import './expression.scss'; diff --git a/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx b/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx index 918c87c5d13a8..aee314a86cd78 100644 --- a/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx +++ b/x-pack/plugins/stack_alerts/public/rule_types/threshold/visualization.tsx @@ -33,7 +33,8 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ChartsPluginSetup } from '@kbn/charts-plugin/public'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { AggregationType, Comparator } from '@kbn/triggers-actions-ui-plugin/public'; +import { AggregationType } from '@kbn/triggers-actions-ui-plugin/public'; +import type { Comparator } from '@kbn/alerting-comparators'; import { parseDuration } from '@kbn/alerting-plugin/common/parse_duration'; import { i18n } from '@kbn/i18n'; import { diff --git a/x-pack/plugins/stack_alerts/tsconfig.json b/x-pack/plugins/stack_alerts/tsconfig.json index a765291e89d98..b005183198f32 100644 --- a/x-pack/plugins/stack_alerts/tsconfig.json +++ b/x-pack/plugins/stack_alerts/tsconfig.json @@ -51,6 +51,7 @@ "@kbn/esql-utils", "@kbn/data-view-utils", "@kbn/search-types", + "@kbn/alerting-comparators" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/task_manager/tsconfig.json b/x-pack/plugins/task_manager/tsconfig.json index d1accb045d526..2c265a93e55a1 100644 --- a/x-pack/plugins/task_manager/tsconfig.json +++ b/x-pack/plugins/task_manager/tsconfig.json @@ -10,7 +10,6 @@ "common/**/*" ], "kbn_references": [ - "@kbn/alerting-state-types", "@kbn/core", "@kbn/usage-collection-plugin", "@kbn/config-schema", @@ -22,7 +21,8 @@ "@kbn/core-saved-objects-common", "@kbn/core-saved-objects-utils-server", "@kbn/core-test-helpers-kbn-server", - "@kbn/core-saved-objects-server" + "@kbn/core-saved-objects-server", + "@kbn/alerting-state-types" ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index f73cbc46de222..f68a59f484638 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -21654,7 +21654,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "Activez cette option pour déclencher l’action si un groupe précédemment détecté cesse de signaler des résultats. Ce n’est pas recommandé pour les infrastructures à montée en charge dynamique qui peuvent rapidement lancer ou stopper des nœuds automatiquement.", "xpack.infra.metrics.alertFlyout.noDataHelpText": "Activez cette option pour déclencher l'action si l'indicateur ou les indicateurs ne signale(nt) aucune donnée sur la période attendue, ou si l'alerte ne parvient pas à interroger Elasticsearch", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "Apprenez comment ajouter davantage de données", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "N'est pas entre", "xpack.infra.metrics.alertFlyout.removeCondition": "Retirer la condition", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "Retirer le seuil d'avertissement", "xpack.infra.metrics.alertFlyout.warningThreshold": "Avertissement", @@ -29972,7 +29971,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "Les seuils doivent contenir un nombre valide.", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "Durée obligatoire.", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "Activez cette option pour déclencher l’action si un groupe précédemment détecté cesse de signaler des résultats. Ce n’est pas recommandé pour les infrastructures à montée en charge dynamique qui peuvent rapidement lancer ou stopper des nœuds automatiquement.", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "N'est pas entre", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "Retirer la condition", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "Rechercher des données d'observabilité… (par exemple host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "Sélectionner une vue de données", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 42ef3c2abf137..e7d393cd5595c 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21628,7 +21628,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "以前に検出されたグループが結果を報告しなくなった場合は、これを有効にすると、アクションがトリガーされます。自動的に急速にノードを開始および停止することがある動的に拡張するインフラストラクチャーでは、これは推奨されません。", "xpack.infra.metrics.alertFlyout.noDataHelpText": "有効にすると、メトリックが想定された期間内にデータを報告しない場合、またはアラートがElasticsearchをクエリできない場合に、アクションをトリガーします", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "データの追加方法", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "is not between", "xpack.infra.metrics.alertFlyout.removeCondition": "条件を削除", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "warningThresholdを削除", "xpack.infra.metrics.alertFlyout.warningThreshold": "警告", @@ -29944,7 +29943,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "しきい値には有効な数値を含める必要があります。", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "ページサイズが必要です。", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "以前に検出されたグループが結果を報告しなくなった場合は、これを有効にすると、アクションがトリガーされます。自動的に急速にノードを開始および停止することがある動的に拡張するインフラストラクチャーでは、これは推奨されません。", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "is not between", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "条件を削除", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "オブザーバビリティデータを検索…(例:host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "データビューを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d35e005f3bb61..10435bf1bdfc8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21661,7 +21661,6 @@ "xpack.infra.metrics.alertFlyout.groupDisappearHelpText": "启用此选项可在之前检测的组开始不报告任何数据时触发操作。不建议将此选项用于可能会快速自动启动和停止节点的动态扩展基础架构。", "xpack.infra.metrics.alertFlyout.noDataHelpText": "启用此选项可在指标在预期的时间段中未报告任何数据时或告警无法查询 Elasticsearch 时触发操作", "xpack.infra.metrics.alertFlyout.ofExpression.popoverLinkLabel": "了解如何添加更多数据", - "xpack.infra.metrics.alertFlyout.outsideRangeLabel": "不介于", "xpack.infra.metrics.alertFlyout.removeCondition": "删除条件", "xpack.infra.metrics.alertFlyout.removeWarningThreshold": "移除警告阈值", "xpack.infra.metrics.alertFlyout.warningThreshold": "警告", @@ -29985,7 +29984,6 @@ "xpack.observability.customThreshold.rule.alertFlyout.error.thresholdTypeRequired": "阈值必须包含有效数字。", "xpack.observability.customThreshold.rule.alertFlyout.error.timeRequred": "“时间大小”必填。", "xpack.observability.customThreshold.rule.alertFlyout.groupDisappearHelpText": "启用此选项可在之前检测的组开始不报告任何数据时触发操作。不建议将此选项用于可能会快速自动启动和停止节点的动态扩展基础架构。", - "xpack.observability.customThreshold.rule.alertFlyout.outsideRangeLabel": "不介于", "xpack.observability.customThreshold.rule.alertFlyout.removeCondition": "删除条件", "xpack.observability.customThreshold.rule.alertFlyout.searchBar.placeholder": "搜索 Observability 数据……(例如 host.name:host-1)", "xpack.observability.customThreshold.rule.alertFlyout.selectDataViewPrompt": "选择数据视图", diff --git a/x-pack/plugins/triggers_actions_ui/kibana.jsonc b/x-pack/plugins/triggers_actions_ui/kibana.jsonc index 928ae6f648f01..66fcd64dabb93 100644 --- a/x-pack/plugins/triggers_actions_ui/kibana.jsonc +++ b/x-pack/plugins/triggers_actions_ui/kibana.jsonc @@ -10,7 +10,6 @@ "xpack", "trigger_actions_ui" ], - "requiredPlugins": [ "management", "charts", @@ -46,7 +45,7 @@ ], "extraPublicDirs": [ "public/common", - "public/common/constants" + "public/common/constants", ] } -} +} \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts b/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts deleted file mode 100644 index 0bec60efab15d..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/common/constants/comparators.ts +++ /dev/null @@ -1,69 +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 { Comparator } from '../types'; - -export enum COMPARATORS { - GREATER_THAN = '>', - GREATER_THAN_OR_EQUALS = '>=', - BETWEEN = 'between', - LESS_THAN = '<', - LESS_THAN_OR_EQUALS = '<=', - NOT_BETWEEN = 'notBetween', -} - -export const builtInComparators: { [key: string]: Comparator } = { - [COMPARATORS.GREATER_THAN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isAboveLabel', { - defaultMessage: 'Is above', - }), - value: COMPARATORS.GREATER_THAN, - requiredValues: 1, - }, - [COMPARATORS.GREATER_THAN_OR_EQUALS]: { - text: i18n.translate( - 'xpack.triggersActionsUI.common.constants.comparators.isAboveOrEqualsLabel', - { - defaultMessage: 'Is above or equals', - } - ), - value: COMPARATORS.GREATER_THAN_OR_EQUALS, - requiredValues: 1, - }, - [COMPARATORS.LESS_THAN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBelowLabel', { - defaultMessage: 'Is below', - }), - value: COMPARATORS.LESS_THAN, - requiredValues: 1, - }, - [COMPARATORS.LESS_THAN_OR_EQUALS]: { - text: i18n.translate( - 'xpack.triggersActionsUI.common.constants.comparators.isBelowOrEqualsLabel', - { - defaultMessage: 'Is below or equals', - } - ), - value: COMPARATORS.LESS_THAN_OR_EQUALS, - requiredValues: 1, - }, - [COMPARATORS.BETWEEN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBetweenLabel', { - defaultMessage: 'Is between', - }), - value: COMPARATORS.BETWEEN, - requiredValues: 2, - }, - [COMPARATORS.NOT_BETWEEN]: { - text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isNotBetweenLabel', { - defaultMessage: 'Not between', - }), - value: COMPARATORS.NOT_BETWEEN, - requiredValues: 2, - }, -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts index c9437c21c12ad..1398b33e787b8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/constants/index.ts @@ -5,7 +5,9 @@ * 2.0. */ -export { COMPARATORS, builtInComparators } from './comparators'; +import { Comparator, COMPARATORS } from '@kbn/alerting-comparators'; +import { i18n } from '@kbn/i18n'; + export { AGGREGATION_TYPES, builtInAggregationTypes } from './aggregation_types'; export { loadAllActions, loadActionTypes } from '../../application/lib/action_connector_api'; export { ConnectorAddModal } from '../../application/sections/action_connector_form'; @@ -20,3 +22,54 @@ export const PLUGIN_ID = 'triggersActions'; export const ALERTS_PAGE_ID = 'triggersActionsAlerts'; export const CONNECTORS_PLUGIN_ID = 'triggersActionsConnectors'; export * from './i18n_weekdays'; + +export const builtInComparators: { [key: string]: Comparator } = { + [COMPARATORS.GREATER_THAN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isAboveLabel', { + defaultMessage: 'Is above', + }), + value: COMPARATORS.GREATER_THAN, + requiredValues: 1, + }, + [COMPARATORS.GREATER_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.triggersActionsUI.common.constants.comparators.isAboveOrEqualsLabel', + { + defaultMessage: 'Is above or equals', + } + ), + value: COMPARATORS.GREATER_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBelowLabel', { + defaultMessage: 'Is below', + }), + value: COMPARATORS.LESS_THAN, + requiredValues: 1, + }, + [COMPARATORS.LESS_THAN_OR_EQUALS]: { + text: i18n.translate( + 'xpack.triggersActionsUI.common.constants.comparators.isBelowOrEqualsLabel', + { + defaultMessage: 'Is below or equals', + } + ), + value: COMPARATORS.LESS_THAN_OR_EQUALS, + requiredValues: 1, + }, + [COMPARATORS.BETWEEN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isBetweenLabel', { + defaultMessage: 'Is between', + }), + value: COMPARATORS.BETWEEN, + requiredValues: 2, + }, + [COMPARATORS.NOT_BETWEEN]: { + text: i18n.translate('xpack.triggersActionsUI.common.constants.comparators.isNotBetweenLabel', { + defaultMessage: 'Is not between', + }), + value: COMPARATORS.NOT_BETWEEN, + requiredValues: 2, + }, +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx index 7e8eb5d6feee0..02ee05c125db6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.test.tsx @@ -49,7 +49,7 @@ describe('threshold expression', () => { "value": "between", }, Object { - "text": "Not between", + "text": "Is not between", "value": "notBetween", }, ] diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx index fdbd0f6bc7022..5e24ced438a68 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx @@ -18,8 +18,8 @@ import { EuiText, } from '@elastic/eui'; import { isNil } from 'lodash'; +import { Comparator } from '@kbn/alerting-comparators'; import { builtInComparators } from '../constants'; -import { Comparator } from '../types'; import { IErrorObject } from '../../types'; import { ClosablePopoverTitle } from './components'; diff --git a/x-pack/plugins/triggers_actions_ui/public/common/index.ts b/x-pack/plugins/triggers_actions_ui/public/common/index.ts index 52b39919227ea..de048e4b2e57c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/index.ts @@ -15,18 +15,12 @@ export { OfExpression, ThresholdExpression, } from './expression_items'; -export { - COMPARATORS, - builtInComparators, - builtInAggregationTypes, - builtInGroupByTypes, -} from './constants'; +export { builtInComparators, builtInAggregationTypes, builtInGroupByTypes } from './constants'; export { connectorDeprecatedMessage, deprecatedMessage } from './connectors_selection'; export type { IOption } from './index_controls'; export { getFields, getIndexOptions, firstFieldOption } from './index_controls'; export { getTimeFieldOptions, getTimeOptions, useKibana } from './lib'; export type { - Comparator, AggregationType, GroupByType, RuleStatus, diff --git a/x-pack/plugins/triggers_actions_ui/public/common/types.ts b/x-pack/plugins/triggers_actions_ui/public/common/types.ts index 2bf6d601ff476..c22332a9ad028 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/common/types.ts @@ -7,12 +7,6 @@ import { KBN_FIELD_TYPES } from '@kbn/field-types'; -export interface Comparator { - text: string; - value: string; - requiredValues: number; -} - export type ValidNormalizedTypes = `${Exclude< KBN_FIELD_TYPES, | KBN_FIELD_TYPES.UNKNOWN diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index f4276eb8a891b..3f00b4ffb8ae5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -102,7 +102,7 @@ export function plugin(context: PluginInitializerContext) { } export { useKibana } from './common'; -export type { AggregationType, Comparator, ValidNormalizedTypes } from './common'; +export type { AggregationType, ValidNormalizedTypes } from './common'; export { WhenExpression, @@ -119,7 +119,6 @@ export { getTimeFieldOptions, getTimeOptions, GroupByExpression, - COMPARATORS, connectorDeprecatedMessage, deprecatedMessage, } from './common'; diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index 12cdcbdfe1442..bb6ad80dd27c9 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -66,7 +66,8 @@ "@kbn/react-kibana-mount", "@kbn/react-kibana-context-theme", "@kbn/controls-plugin", - "@kbn/search-types" + "@kbn/search-types", + "@kbn/alerting-comparators" ], "exclude": ["target/**/*"] } diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts index 65a14577f06ea..dc6197320ecf0 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_fired.ts @@ -7,14 +7,12 @@ import { omit } from 'lodash'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { waitForAlertInIndex, @@ -122,7 +120,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts index 8892fee26a28e..f552eca84b8a4 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -6,14 +6,12 @@ */ import { omit } from 'lodash'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { NO_DATA_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; @@ -92,7 +90,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts index fac69cd18a9bb..391acf747eb25 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/avg_us_fired.ts @@ -8,13 +8,11 @@ import moment from 'moment'; import { ApmSynthtraceEsClient } from '@kbn/apm-synthtrace'; import { format } from 'url'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; @@ -104,7 +102,7 @@ export default function ({ getService }: FtrProviderContext) { criteria: [ { aggType: 'custom', - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [7500000], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index b4c39683fade9..56370d31a38f7 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -6,10 +6,8 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -120,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', @@ -220,7 +218,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts index 1c20df4f014f5..5c56dbdf9e808 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/documents_count_fired.ts @@ -8,13 +8,11 @@ import { omit } from 'lodash'; import expect from '@kbn/expect'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { @@ -124,7 +122,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', @@ -223,7 +221,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts index b5aa7c4d03904..fcb5fac564df4 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/group_by_fired.ts @@ -6,13 +6,11 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createIndexConnector, createRule } from '../helpers/alerting_api_helper'; import { createDataView, deleteDataView } from '../helpers/data_view'; import { @@ -120,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [0.2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts index 46a6874d34588..b76c09c12642d 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/p99_pct_fired.ts @@ -7,10 +7,8 @@ import { omit } from 'lodash'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -119,7 +117,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts index da613a8a4fb7c..d56310dd9b0d8 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule/rate_bytes_fired.ts @@ -6,10 +6,8 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -118,7 +116,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [50_000], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts index 9ecccffbd596e..0565d759df20d 100644 --- a/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts +++ b/x-pack/test/alerting_api_integration/observability/custom_threshold_rule_data_view.ts @@ -6,12 +6,10 @@ */ import expect from '@kbn/expect'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../common/ftr_provider_context'; import { getUrlPrefix, ObjectRemover } from '../common/lib'; import { createRule } from './helpers/alerting_api_helper'; @@ -78,7 +76,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [7500000], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts index 808df288b094e..bc36566f91f52 100644 --- a/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts +++ b/x-pack/test/alerting_api_integration/observability/metric_threshold_rule.ts @@ -10,10 +10,10 @@ import expect from '@kbn/expect'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; import { Aggregators, - Comparator, InfraRuleType, MetricThresholdParams, } from '@kbn/infra-plugin/common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { waitForDocumentInIndex, waitForAlertInIndex, @@ -86,7 +86,7 @@ export default function ({ getService }: FtrProviderContext) { criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', @@ -227,7 +227,7 @@ export default function ({ getService }: FtrProviderContext) { `https://localhost:5601/app/observability/alerts?_a=(kuery:%27kibana.alert.uuid:%20%22${alertId}%22%27%2CrangeFrom:%27${rangeFrom}%27%2CrangeTo:now%2Cstatus:all)` ); expect(resp.hits.hits[0]._source?.reason).eql( - `system.cpu.user.pct is 90% in the last 5 mins. Alert when > 50%.` + `system.cpu.user.pct is 90% in the last 5 mins. Alert when above 50%.` ); }); }); diff --git a/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts b/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts index f76dd0e63fd87..4a60158c77e80 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/inventory_threshold_alert.ts @@ -6,10 +6,11 @@ */ import expect from '@kbn/expect'; -import { Comparator, InventoryMetricConditions } from '@kbn/infra-plugin/common/alerting/metrics'; +import { InventoryMetricConditions } from '@kbn/infra-plugin/common/alerting/metrics'; import { InventoryItemType, SnapshotMetricType } from '@kbn/metrics-data-access-plugin/common'; import { evaluateCondition } from '@kbn/infra-plugin/server/lib/alerting/inventory_metric_threshold/evaluate_condition'; import { InfraSource } from '@kbn/infra-plugin/server/lib/sources'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; import { DATES } from './constants'; import { createFakeLogger } from './create_fake_logger'; @@ -26,7 +27,7 @@ export default function ({ getService }: FtrProviderContext) { timeUnit: 'm', sourceId: 'default', threshold: [100], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, }; const source: InfraSource = { @@ -207,7 +208,7 @@ export default function ({ getService }: FtrProviderContext) { ...baseCondition, metric: 'rx', threshold: [107374182400], - comparator: Comparator.LT, + comparator: COMPARATORS.LESS_THAN, }, esClient, }); diff --git a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts index 18d46a45ded9e..96aa8f91a2c9c 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/metric_threshold_alert.ts @@ -9,7 +9,6 @@ import expect from '@kbn/expect'; import moment from 'moment'; import { Aggregators, - Comparator, CountMetricExpressionParams, CustomMetricExpressionParams, NonCountMetricExpressionParams, @@ -19,6 +18,7 @@ import { EvaluatedRuleParams, evaluateRule, } from '@kbn/infra-plugin/server/lib/alerting/metric_threshold/lib/evaluate_rule'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; import { DATES } from './constants'; import { createFakeLogger } from './create_fake_logger'; @@ -38,7 +38,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.SUM, metric: 'value', } as NonCountMetricExpressionParams, @@ -89,7 +89,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [10000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -144,7 +144,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.CUSTOM, customMetrics: [ { name: 'A', aggType: 'count', filter: 'event.dataset: "apache2.error"' }, @@ -214,7 +214,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [20000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -270,7 +270,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [20000], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -349,7 +349,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -432,7 +432,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [2], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -496,7 +496,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -591,7 +591,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -645,7 +645,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -752,7 +752,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -803,7 +803,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [0], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -897,7 +897,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.COUNT, } as CountMetricExpressionParams, ], @@ -1083,7 +1083,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 5, timeUnit: 'm', threshold: [100], - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, aggType: Aggregators.SUM, metric: 'value', } as NonCountMetricExpressionParams, @@ -1183,7 +1183,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [107374182400], - comparator: Comparator.LT_OR_EQ, + comparator: COMPARATORS.LESS_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, @@ -1236,7 +1236,7 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [0.5], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, @@ -1291,9 +1291,9 @@ export default function ({ getService }: FtrProviderContext) { timeSize: 1, timeUnit: 'm', threshold: [1], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, warningThreshold: [0.5], - warningComparator: Comparator.GT_OR_EQ, + warningComparator: COMPARATORS.GREATER_THAN_OR_EQUALS, aggType: Aggregators.RATE, metric: 'value', } as NonCountMetricExpressionParams, diff --git a/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts b/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts index 2e7a900e0fe87..a091a912f8cb5 100644 --- a/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts +++ b/x-pack/test/api_integration/apis/metrics_ui/metrics_alerting.ts @@ -7,8 +7,9 @@ import expect from '@kbn/expect'; import moment from 'moment'; -import { Comparator, MetricExpressionParams } from '@kbn/infra-plugin/common/alerting/metrics'; +import { MetricExpressionParams } from '@kbn/infra-plugin/common/alerting/metrics'; import { getElasticsearchMetricQuery } from '@kbn/infra-plugin/server/lib/alerting/metric_threshold/lib/metric_query'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -19,7 +20,7 @@ export default function ({ getService }: FtrProviderContext) { aggType, timeUnit: 'm', threshold: [0], - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, timeSize: 5, ...(aggType !== 'count' ? { metric: 'test.metric' } : {}), } as MetricExpressionParams); diff --git a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts index 4e041c18f4ea7..571594a8b94b6 100644 --- a/x-pack/test/functional/apps/infra/metrics_source_configuration.ts +++ b/x-pack/test/functional/apps/infra/metrics_source_configuration.ts @@ -9,11 +9,11 @@ import { cleanup, Dataset, generate, PartialConfig } from '@kbn/data-forge'; import expect from '@kbn/expect'; import { Aggregators, - Comparator, InfraRuleType, MetricThresholdParams, } from '@kbn/infra-plugin/common/alerting/metrics'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { createRule } from '../../../alerting_api_integration/observability/helpers/alerting_api_helper'; import { waitForDocumentInIndex, @@ -192,7 +192,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { criteria: [ { aggType: Aggregators.AVERAGE, - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json index 2df784cd0c8ba..e86c9b1039faf 100644 --- a/x-pack/test/tsconfig.json +++ b/x-pack/test/tsconfig.json @@ -123,7 +123,6 @@ "@kbn/discover-plugin", "@kbn/files-plugin", "@kbn/shared-ux-file-types", - "@kbn/alerting-state-types", "@kbn/assetManager-plugin", "@kbn/guided-onboarding-plugin", "@kbn/field-formats-plugin", @@ -175,6 +174,8 @@ "@kbn/esql-utils", "@kbn/search-types", "@kbn/analytics-ftr-helpers-plugin", - "@kbn/reporting-server", + "@kbn/alerting-comparators", + "@kbn/alerting-state-types", + "@kbn/reporting-server" ] } diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts index 3f3e9b8223a8d..312792e33d9d3 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -109,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts index 6f7bfec08e919..50772ee105704 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/avg_pct_no_data.ts @@ -5,15 +5,13 @@ * 2.0. */ -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { NO_DATA_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -83,7 +81,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts index 5ba84e3f51401..9e4b0be513945 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/custom_eq_avg_bytes_fired.ts @@ -12,10 +12,8 @@ */ import { cleanup, Dataset, generate, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; @@ -112,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', @@ -212,7 +210,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.9], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts index 4f26d2caf5c52..589ebe7a29fa2 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/documents_count_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -113,7 +111,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', @@ -211,7 +209,7 @@ export default function ({ getService }: FtrProviderContext) { .eql({ criteria: [ { - comparator: Comparator.OUTSIDE_RANGE, + comparator: COMPARATORS.NOT_BETWEEN, threshold: [1, 2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts index 3dbb64f471b5d..08baeb3a96eb3 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/group_by_fired.ts @@ -13,13 +13,11 @@ import { kbnTestConfig } from '@kbn/test'; import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ActionDocument } from './typings'; @@ -113,7 +111,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT_OR_EQ, + comparator: COMPARATORS.GREATER_THAN_OR_EQUALS, threshold: [0.2], timeSize: 1, timeUnit: 'm', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts index 884259f107109..f21dc4a3c08ba 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/custom_threshold_rule/p99_pct_fired.ts @@ -6,15 +6,13 @@ */ import { cleanup, generate, Dataset, PartialConfig } from '@kbn/data-forge'; -import { - Aggregators, - Comparator, -} from '@kbn/observability-plugin/common/custom_threshold_rule/types'; +import { Aggregators } from '@kbn/observability-plugin/common/custom_threshold_rule/types'; import { FIRED_ACTIONS_ID } from '@kbn/observability-plugin/server/lib/rules/custom_threshold/constants'; import expect from '@kbn/expect'; import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import { parseSearchParams } from '@kbn/share-plugin/common/url_service'; import { omit } from 'lodash'; +import { COMPARATORS } from '@kbn/alerting-comparators'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { ISO_DATE_REGEX } from './constants'; import { ActionDocument, LogsExplorerLocatorParsedParams } from './typings'; @@ -112,7 +110,7 @@ export default function ({ getService }: FtrProviderContext) { params: { criteria: [ { - comparator: Comparator.GT, + comparator: COMPARATORS.GREATER_THAN, threshold: [0.5], timeSize: 5, timeUnit: 'm', diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 020b861654dc9..639a092b6f45d 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -99,6 +99,7 @@ "@kbn/utility-types", "@kbn/synthetics-plugin", "@kbn/dataset-quality-plugin", + "@kbn/alerting-comparators", "@kbn/search-types" ] } diff --git a/yarn.lock b/yarn.lock index ecd4bf656b872..caa1bc8afa0b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3107,6 +3107,10 @@ version "0.0.0" uid "" +"@kbn/alerting-comparators@link:x-pack/packages/kbn-alerting-comparators": + version "0.0.0" + uid "" + "@kbn/alerting-example-plugin@link:x-pack/examples/alerting_example": version "0.0.0" uid "" From d7a095fb745c39185f583e67cfebd4fc7eba0a20 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 28 May 2024 16:40:56 +0300 Subject: [PATCH 34/59] fix: [Obs Applications > Services][AXE-CORE]: Interactive elements must not be nested in other interactive elements (#184219) Closes: https://github.com/elastic/observability-dev/issues/3409 ## Description The Obs Applications > Services view has a "What are these metrics?" icon that exposes a tooltip when hovered, but that tooltip cannot take keyboard focus, making it unavailable to keyboard users. Screenshot attached below. ### Steps to recreate 1. Open the [Obs Services](https://keepserverless-qa-oblt-b4ba07.kb.eu-west-1.aws.qa.elastic.cloud/app/apm/services) view 2. Tab through the blue notification box until Learn More button has focus 3. Tab again and verify focus lands on the text "Learn More" inside the button. This is a nested link. ### What was done?: 1. Removed extra `EuiLink`. ### Screen: https://github.com/elastic/kibana/assets/20072247/577b1b91-9920-4dec-a491-855cfcd03d8f Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/shared/ml_callout/index.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx b/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx index 404e9dcba13b6..0a6058a2c28c6 100644 --- a/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx +++ b/x-pack/plugins/observability_solution/apm/public/components/shared/ml_callout/index.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; import { AnomalyDetectionSetupState } from '../../../../common/anomaly_detection/get_anomaly_detection_setup_state'; import { useMlManageJobsHref } from '../../../hooks/use_ml_manage_jobs_href'; -import { LegacyAPMLink } from '../links/apm/apm_link'; +import { useAPMHref } from '../links/apm/apm_link'; export function shouldDisplayMlCallout(anomalyDetectionSetupState: AnomalyDetectionSetupState) { return ( @@ -35,7 +35,7 @@ export function MLCallout({ append?: React.ReactElement; }) { const [loading, setLoading] = useState(false); - + const apmGetLearnMoreHref = useAPMHref({ path: '/settings/anomaly-detection' }); const mlManageJobsHref = useMlManageJobsHref(); let properties: @@ -48,19 +48,19 @@ export function MLCallout({ } | undefined; - const getLearnMoreLink = (color: 'primary' | 'success') => ( - - { + return ( + {i18n.translate('xpack.apm.mlCallout.learnMoreButton', { defaultMessage: `Learn more`, })} - - - ); + + ); + }; switch (anomalyDetectionSetupState) { case AnomalyDetectionSetupState.NoJobs: From 574fe0a436976947cbee4f8dcbeed1e2c6636752 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 28 May 2024 07:55:32 -0600 Subject: [PATCH 35/59] [ES|QL] accept null values for function arguments (#184254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Accepts null values for all function and operator arguments. Screenshot 2024-05-24 at 11 04 43 AM ### 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 Co-authored-by: Stratoula Kalafateli --- .../generate_function_validation_tests.ts | 82 +- .../src/definitions/builtin.ts | 21 - .../src/definitions/helpers.ts | 2 - .../src/shared/helpers.ts | 7 +- .../esql_validation_meta_tests.json | 976 +++++++++++++++++- .../src/validation/helpers.ts | 4 + .../src/validation/validation.test.ts | 202 ++++ 7 files changed, 1244 insertions(+), 50 deletions(-) diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts index bcf11337117f4..bc12ea1fbfb2f 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_validation_tests.ts @@ -24,6 +24,7 @@ import { isSupportedFieldType, } from '../src/definitions/types'; import { FUNCTION_DESCRIBE_BLOCK_NAME } from '../src/validation/function_describe_block_name'; +import { getMaxMinNumberOfParams } from '../src/validation/helpers'; export const fieldNameFromType = (type: SupportedFieldType) => `${camelCase(type)}Field`; @@ -51,6 +52,7 @@ function generateTestsForEvalFunction(definition: FunctionDefinition) { generateWhereCommandTestsForEvalFunction(definition, testCases); generateEvalCommandTestsForEvalFunction(definition, testCases); generateSortCommandTestsForEvalFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } @@ -60,6 +62,7 @@ function generateTestsForAggFunction(definition: FunctionDefinition) { generateSortCommandTestsForAggFunction(definition, testCases); generateWhereCommandTestsForAggFunction(definition, testCases); generateEvalCommandTestsForAggFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } @@ -67,9 +70,60 @@ function generateTestsForGroupingFunction(definition: FunctionDefinition) { const testCases: Map = new Map(); generateStatsCommandTestsForGroupingFunction(definition, testCases); generateSortCommandTestsForGroupingFunction(definition, testCases); + generateNullAcceptanceTestsForFunction(definition, testCases); return testCases; } +function generateNullAcceptanceTestsForFunction( + definition: FunctionDefinition, + testCases: Map +) { + const { max, min } = getMaxMinNumberOfParams(definition); + const numberOfArgsToTest = max === Infinity ? min : max; + const signatureWithGreatestNumberOfParams = definition.signatures.find( + (signature) => signature.params.length === numberOfArgsToTest + )!; + + const commandToTestWith = definition.supportedCommands.includes('eval') ? 'eval' : 'stats'; + + // test that the function accepts nulls + testCases.set( + `from a_index | ${commandToTestWith} ${ + getFunctionSignatures( + { + ...definition, + signatures: [ + { + ...signatureWithGreatestNumberOfParams, + params: new Array(numberOfArgsToTest).fill({ name: 'null' }), + }, + ], + }, + { withTypes: false } + )[0].declaration + }`, + [] + ); + + testCases.set( + `row nullVar = null | ${commandToTestWith} ${ + getFunctionSignatures( + { + ...definition, + signatures: [ + { + ...signatureWithGreatestNumberOfParams, + params: new Array(numberOfArgsToTest).fill({ name: 'nullVar' }), + }, + ], + }, + { withTypes: false } + )[0].declaration + }`, + [] + ); +} + function generateRowCommandTestsForEvalFunction( { name, alias, signatures, ...defRest }: FunctionDefinition, testCases: Map @@ -262,9 +316,11 @@ function generateWhereCommandTestsForAggFunction( } function generateEvalCommandTestsForEvalFunction( - { name, signatures, alias, ...defRest }: FunctionDefinition, + definition: FunctionDefinition, testCases: Map ) { + const { name, signatures, alias, ...defRest } = definition; + for (const { params, ...signRest } of signatures) { const fieldMapping = getFieldMapping(params); testCases.set( @@ -401,26 +457,10 @@ function generateEvalCommandTestsForEvalFunction( // test that additional args are spotted - const getNumberOfParams = (signature: FunctionDefinition['signatures'][number]) => ({ - all: signature.params.length, - required: signature.params.filter(({ optional }) => !optional).length, - }); - - // get the signature with the greatest number of params - const [first, ...rest] = signatures; - let signatureWithGreatestNumberOfParams = first; - let { all: maxNumberOfArgs, required: minNumberOfArgs } = getNumberOfParams(first); - - for (const signature of rest) { - const numberOfParams = signature.params.length; - if (numberOfParams > signatureWithGreatestNumberOfParams.params.length) { - signatureWithGreatestNumberOfParams = signature; - } - - maxNumberOfArgs = Math.max(maxNumberOfArgs, numberOfParams); - const numberOfRequiredParams = signature.params.filter(({ optional }) => !optional).length; - minNumberOfArgs = Math.min(minNumberOfArgs, numberOfRequiredParams); - } + const { max: maxNumberOfArgs, min: minNumberOfArgs } = getMaxMinNumberOfParams(definition); + const signatureWithGreatestNumberOfParams = signatures.find( + (signature) => signature.params.length === maxNumberOfArgs + )!; const fieldMappingWithOneExtraArg = getFieldMapping( signatureWithGreatestNumberOfParams.params diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts index 67fe23bada50f..b430e6e7665df 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/builtin.ts @@ -456,27 +456,6 @@ const logicFunctions: FunctionDefinition[] = [ ], returnType: 'boolean', }, - { - params: [ - { name: 'left', type: 'null' }, - { name: 'right', type: 'boolean' }, - ], - returnType: 'boolean', - }, - { - params: [ - { name: 'left', type: 'boolean' }, - { name: 'right', type: 'null' }, - ], - returnType: 'boolean', - }, - { - params: [ - { name: 'left', type: 'null' }, - { name: 'right', type: 'null' }, - ], - returnType: 'boolean', - }, ], })); diff --git a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts index 0380a07385a73..7d70d1acc9631 100644 --- a/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/definitions/helpers.ts @@ -95,12 +95,10 @@ export function printArguments( name, type, optional, - reference, }: { name: string; type: string | string[]; optional?: boolean; - reference?: string; }, withTypes: boolean ): string { diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts index e17788eba21b8..73f60dffb6336 100644 --- a/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/shared/helpers.ts @@ -217,12 +217,17 @@ export function getCommandOption(optionName: CommandOptionsDefinition['name']) { } function compareLiteralType(argType: string, item: ESQLLiteral) { + if (item.literalType === 'null') { + return true; + } + if (item.literalType !== 'string') { if (argType === item.literalType) { return true; } return false; } + if (argType === 'chrono_literal') { return chronoLiterals.some(({ name }) => name === item.text); } @@ -423,7 +428,7 @@ export function isEqualType( } const wrappedTypes = Array.isArray(validHit.type) ? validHit.type : [validHit.type]; // if final type is of type any make it pass for now - return wrappedTypes.some((ct) => ct === 'any' || argType === ct); + return wrappedTypes.some((ct) => ['any', 'null'].includes(ct) || argType === ct); } } diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json index 89c267dd01f2d..623d854b41b4a 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json +++ b/packages/kbn-esql-validation-autocomplete/src/validation/esql_validation_meta_tests.json @@ -9014,6 +9014,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_diff(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_diff(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = abs(5)", "error": [], @@ -9120,6 +9130,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval abs(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval abs(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = acos(5)", "error": [], @@ -9226,6 +9246,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval acos(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval acos(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = asin(5)", "error": [], @@ -9332,6 +9362,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval asin(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval asin(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = atan(5)", "error": [], @@ -9438,6 +9478,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval atan(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval atan(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = atan2(5, 5)", "error": [], @@ -9543,6 +9593,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval atan2(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval atan2(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = case(true, \"a\")", "error": [], @@ -9575,6 +9635,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval case(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval case(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = ceil(5)", "error": [], @@ -9681,6 +9751,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ceil(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ceil(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cidr_match(to_ip(\"127.0.0.1\"), \"a\")", "error": [], @@ -9758,6 +9838,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cidr_match(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cidr_match(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = coalesce(\"a\")", "error": [], @@ -10463,6 +10553,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval coalesce(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval coalesce(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = concat(\"a\", \"a\")", "error": [], @@ -10561,6 +10661,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval concat(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval concat(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cos(5)", "error": [], @@ -10667,6 +10777,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cos(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cos(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = cosh(5)", "error": [], @@ -10773,6 +10893,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval cosh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cosh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_extract(\"ALIGNED_DAY_OF_WEEK_IN_MONTH\", now())", "error": [], @@ -10839,6 +10969,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_extract(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_extract(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_format(\"a\", now())", "error": [], @@ -10904,6 +11044,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_format(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_format(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_parse(\"a\", \"a\")", "error": [], @@ -10998,6 +11148,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval date_parse(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_parse(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = date_trunc(1 year, now())", "error": [], @@ -11089,6 +11249,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval date_trunc(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval date_trunc(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = e()", "error": [], @@ -11126,6 +11296,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval e()", + "error": [], + "warning": [] + }, { "query": "row var = ends_with(\"a\", \"a\")", "error": [], @@ -11210,6 +11385,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ends_with(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ends_with(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = floor(5)", "error": [], @@ -11316,6 +11501,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval floor(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval floor(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = greatest(\"a\")", "error": [], @@ -11639,6 +11834,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval greatest(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval greatest(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = least(\"a\")", "error": [], @@ -11962,6 +12167,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval least(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval least(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = left(\"a\", 5)", "error": [], @@ -12067,6 +12282,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval left(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval left(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = length(\"a\")", "error": [], @@ -12173,6 +12398,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval length(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval length(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = log(5, 5)", "error": [], @@ -12339,6 +12574,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval log(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval log(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = log10(5)", "error": [], @@ -12445,6 +12690,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval log10(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval log10(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = ltrim(\"a\")", "error": [], @@ -12551,6 +12806,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval ltrim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval ltrim(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_avg(5)", "error": [], @@ -12657,6 +12922,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_avg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_avg(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_concat(\"a\", \"a\")", "error": [], @@ -12762,6 +13037,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_concat(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_concat(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_count(\"a\")", "error": [], @@ -13136,6 +13421,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_count(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_count(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_dedupe(\"a\")", "error": [], @@ -13472,6 +13767,26 @@ "error": [], "warning": [] }, + { + "query": "row var = mv_dedupe(to_cartesianpoint(\"POINT (30 10)\"))", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_dedupe(cartesianPointField)", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval mv_dedupe(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_dedupe(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_first(\"a\")", "error": [], @@ -13807,17 +14122,27 @@ "warning": [] }, { - "query": "row var = mv_last(\"a\")", + "query": "from a_index | eval mv_first(null)", "error": [], "warning": [] }, { - "query": "row mv_last(\"a\")", + "query": "row nullVar = null | eval mv_first(nullVar)", "error": [], "warning": [] }, { - "query": "from a_index | eval var = mv_last(stringField)", + "query": "row var = mv_last(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "row mv_last(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "from a_index | eval var = mv_last(stringField)", "error": [], "warning": [] }, @@ -14140,6 +14465,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_last(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_last(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_max(\"a\")", "error": [], @@ -14382,6 +14717,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_max(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_max(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_median(5)", "error": [], @@ -14488,6 +14833,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_median(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_median(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_min(\"a\")", "error": [], @@ -14730,6 +15085,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_min(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_min(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_slice(\"a\", 5, 5)", "error": [], @@ -15161,6 +15526,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_slice(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_slice(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_sort(\"a\", \"asc\")", "error": [], @@ -15332,6 +15707,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval mv_sort(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_sort(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_sum(5)", "error": [], @@ -15438,6 +15823,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_sum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_sum(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = mv_zip(\"a\", \"a\", \"a\")", "error": [], @@ -15564,6 +15959,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval mv_zip(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval mv_zip(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = now()", "error": [], @@ -15596,6 +16001,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval now()", + "error": [], + "warning": [] + }, { "query": "row var = pi()", "error": [], @@ -15633,6 +16043,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval pi()", + "error": [], + "warning": [] + }, { "query": "row var = pow(5, 5)", "error": [], @@ -15738,6 +16153,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval pow(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval pow(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = replace(\"a\", \"a\", \"a\")", "error": [], @@ -15849,6 +16274,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval replace(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval replace(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = right(\"a\", 5)", "error": [], @@ -15954,6 +16389,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval right(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval right(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = round(5, 5)", "error": [], @@ -16120,6 +16565,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval round(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval round(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = rtrim(\"a\")", "error": [], @@ -16226,6 +16681,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval rtrim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval rtrim(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = signum(5)", "error": [], @@ -16332,6 +16797,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval signum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval signum(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sin(5)", "error": [], @@ -16438,6 +16913,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sin(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sin(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sinh(5)", "error": [], @@ -16544,6 +17029,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sinh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sinh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = split(\"a\", \"a\")", "error": [], @@ -16649,6 +17144,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval split(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval split(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = sqrt(5)", "error": [], @@ -16755,6 +17260,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval sqrt(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval sqrt(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_contains(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17153,6 +17668,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_contains(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_contains(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_disjoint(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17551,6 +18076,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_disjoint(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_disjoint(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_intersects(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -17949,6 +18484,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_intersects(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_intersects(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_within(to_geopoint(\"POINT (30 10)\"), to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18347,6 +18892,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_within(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_within(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_x(to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18486,6 +19041,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_x(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_x(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = st_y(to_geopoint(\"POINT (30 10)\"))", "error": [], @@ -18625,6 +19190,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval st_y(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval st_y(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = starts_with(\"a\", \"a\")", "error": [], @@ -18709,6 +19284,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval starts_with(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval starts_with(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = substring(\"a\", 5, 5)", "error": [], @@ -18835,6 +19420,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval substring(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval substring(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tan(5)", "error": [], @@ -18941,6 +19536,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval tan(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval tan(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tanh(5)", "error": [], @@ -19047,6 +19652,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval tanh(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval tanh(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = tau()", "error": [], @@ -19084,6 +19699,11 @@ "error": [], "warning": [] }, + { + "query": "row nullVar = null | eval tau()", + "error": [], + "warning": [] + }, { "query": "row var = to_boolean(\"a\")", "error": [], @@ -19242,6 +19862,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_boolean(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_boolean(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_cartesianpoint(\"a\")", "error": [], @@ -19341,12 +19971,22 @@ "warning": [] }, { - "query": "row var = to_cartesianshape(\"a\")", + "query": "from a_index | eval to_cartesianpoint(null)", "error": [], "warning": [] }, { - "query": "row to_cartesianshape(\"a\")", + "query": "row nullVar = null | eval to_cartesianpoint(nullVar)", + "error": [], + "warning": [] + }, + { + "query": "row var = to_cartesianshape(\"a\")", + "error": [], + "warning": [] + }, + { + "query": "row to_cartesianshape(\"a\")", "error": [], "warning": [] }, @@ -19468,6 +20108,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_cartesianshape(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_cartesianshape(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_datetime(\"a\")", "error": [], @@ -19626,6 +20276,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_datetime(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_datetime(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_degrees(5)", "error": [], @@ -19732,6 +20392,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_degrees(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_degrees(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_double(\"a\")", "error": [], @@ -19957,6 +20627,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_double(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_double(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_geopoint(\"a\")", "error": [], @@ -20055,6 +20735,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_geopoint(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_geopoint(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_geoshape(\"a\")", "error": [], @@ -20183,6 +20873,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_geoshape(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_geoshape(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_integer(\"a\")", "error": [], @@ -20408,6 +21108,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_integer(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_integer(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_ip(\"a\")", "error": [], @@ -20506,6 +21216,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_ip(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_ip(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_long(\"a\")", "error": [], @@ -20691,6 +21411,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_long(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_long(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_lower(\"a\")", "error": [], @@ -20797,6 +21527,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_lower(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_lower(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_radians(5)", "error": [], @@ -20903,6 +21643,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_radians(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_radians(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_string(\"a\")", "error": [], @@ -21377,6 +22127,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_string(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_string(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_unsigned_long(\"a\")", "error": [], @@ -21642,6 +22402,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval to_unsigned_long(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_unsigned_long(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_upper(\"a\")", "error": [], @@ -21748,6 +22518,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_upper(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_upper(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_version(\"a\")", "error": [], @@ -21834,6 +22614,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval to_version(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_version(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = trim(\"a\")", "error": [], @@ -21940,6 +22730,16 @@ ], "warning": [] }, + { + "query": "from a_index | eval trim(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval trim(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = avg(numberField)", "error": [], @@ -22119,6 +22919,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats avg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats avg(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = sum(numberField)", "error": [], @@ -22298,6 +23108,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats sum(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats sum(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = median(numberField)", "error": [], @@ -22477,6 +23297,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats median(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats median(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = median_absolute_deviation(numberField)", "error": [], @@ -22656,6 +23486,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats median_absolute_deviation(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats median_absolute_deviation(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = percentile(numberField, 5)", "error": [], @@ -22835,6 +23675,18 @@ ], "warning": [] }, + { + "query": "from a_index | stats percentile(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats percentile(nullVar, nullVar)", + "error": [ + "Argument of [percentile] must be a constant, received [nullVar]" + ], + "warning": [] + }, { "query": "from a_index | stats var = max(numberField)", "error": [], @@ -23086,6 +23938,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats max(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats max(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = min(numberField)", "error": [], @@ -23337,6 +24199,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats min(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats min(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = count(stringField)", "error": [], @@ -23416,6 +24288,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats count(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats count(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = count_distinct(stringField, numberField)", "error": [], @@ -23495,6 +24377,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats count_distinct(null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats count_distinct(nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = st_centroid_agg(cartesianPointField)", "error": [], @@ -23641,6 +24533,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats st_centroid_agg(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats st_centroid_agg(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats var = values(stringField)", "error": [], @@ -23700,6 +24602,16 @@ ], "warning": [] }, + { + "query": "from a_index | stats values(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats values(nullVar)", + "error": [], + "warning": [] + }, { "query": "from a_index | stats by bucket(dateField, 1 year)", "error": [], @@ -23829,6 +24741,20 @@ ], "warning": [] }, + { + "query": "from a_index | stats bucket(null, null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)", + "error": [ + "Argument of [bucket] must be a constant, received [nullVar]", + "Argument of [bucket] must be a constant, received [nullVar]", + "Argument of [bucket] must be a constant, received [nullVar]" + ], + "warning": [] + }, { "query": "row var = cbrt(5)", "error": [], @@ -23904,6 +24830,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval cbrt(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval cbrt(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = from_base64(\"a\")", "error": [], @@ -23979,6 +24915,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval from_base64(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval from_base64(nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = locate(\"a\", \"a\")", "error": [], @@ -24104,6 +25050,16 @@ "error": [], "warning": [] }, + { + "query": "from a_index | eval locate(null, null, null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval locate(nullVar, nullVar, nullVar)", + "error": [], + "warning": [] + }, { "query": "row var = to_base64(\"a\")", "error": [], @@ -24178,6 +25134,16 @@ "query": "from a_index | sort to_base64(stringField)", "error": [], "warning": [] + }, + { + "query": "from a_index | eval to_base64(null)", + "error": [], + "warning": [] + }, + { + "query": "row nullVar = null | eval to_base64(nullVar)", + "error": [], + "warning": [] } ] } \ No newline at end of file diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts index 1b75dcd965550..20ec8990e1830 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/helpers.ts @@ -45,6 +45,10 @@ export function buildQueryForFieldsForStringSources(queryString: string, ast: ES * Used for too-many, too-few arguments validation */ export function getMaxMinNumberOfParams(definition: FunctionDefinition) { + if (definition.signatures.length === 0) { + return { min: 0, max: 0 }; + } + let min = Infinity; let max = 0; definition.signatures.forEach(({ params, minParams }) => { diff --git a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts index c05e6e2f28787..42879128cf663 100644 --- a/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts +++ b/packages/kbn-esql-validation-autocomplete/src/validation/validation.test.ts @@ -2065,6 +2065,8 @@ describe('validation logic', () => { 'Argument of [date_diff] must be [date], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval date_diff(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_diff(nullVar, nullVar, nullVar)', []); }); describe('abs', () => { @@ -2114,6 +2116,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval abs(booleanField)', [ 'Argument of [abs] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval abs(null)', []); + testErrorsAndWarnings('row nullVar = null | eval abs(nullVar)', []); }); describe('acos', () => { @@ -2163,6 +2167,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval acos(booleanField)', [ 'Argument of [acos] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval acos(null)', []); + testErrorsAndWarnings('row nullVar = null | eval acos(nullVar)', []); }); describe('asin', () => { @@ -2212,6 +2218,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval asin(booleanField)', [ 'Argument of [asin] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval asin(null)', []); + testErrorsAndWarnings('row nullVar = null | eval asin(nullVar)', []); }); describe('atan', () => { @@ -2261,6 +2269,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval atan(booleanField)', [ 'Argument of [atan] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval atan(null)', []); + testErrorsAndWarnings('row nullVar = null | eval atan(nullVar)', []); }); describe('atan2', () => { @@ -2319,6 +2329,8 @@ describe('validation logic', () => { 'Argument of [atan2] must be [number], found value [booleanField] type [boolean]', 'Argument of [atan2] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval atan2(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval atan2(nullVar, nullVar)', []); }); describe('case', () => { @@ -2331,6 +2343,8 @@ describe('validation logic', () => { testErrorsAndWarnings('row var = case(to_cartesianpoint("POINT (30 10)"), true)', [ 'Argument of [case] must be [boolean], found value [to_cartesianpoint("POINT (30 10)")] type [cartesian_point]', ]); + testErrorsAndWarnings('from a_index | eval case(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval case(nullVar, nullVar)', []); }); describe('ceil', () => { @@ -2380,6 +2394,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval ceil(booleanField)', [ 'Argument of [ceil] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ceil(null)', []); + testErrorsAndWarnings('row nullVar = null | eval ceil(nullVar)', []); }); describe('cidr_match', () => { @@ -2425,6 +2441,8 @@ describe('validation logic', () => { 'Argument of [cidr_match] must be [ip], found value [booleanField] type [boolean]', 'Argument of [cidr_match] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cidr_match(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval cidr_match(nullVar, nullVar)', []); }); describe('coalesce', () => { @@ -2704,6 +2722,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort coalesce(numberField)', []); testErrorsAndWarnings('from a_index | eval coalesce(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval coalesce(null)', []); + testErrorsAndWarnings('row nullVar = null | eval coalesce(nullVar)', []); }); describe('concat', () => { @@ -2764,6 +2784,8 @@ describe('validation logic', () => { 'Argument of [concat] must be [string], found value [booleanField] type [boolean]', 'Argument of [concat] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval concat(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval concat(nullVar, nullVar)', []); }); describe('cos', () => { @@ -2813,6 +2835,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval cos(booleanField)', [ 'Argument of [cos] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cos(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cos(nullVar)', []); }); describe('cosh', () => { @@ -2862,6 +2886,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval cosh(booleanField)', [ 'Argument of [cosh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval cosh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cosh(nullVar)', []); }); describe('date_extract', () => { @@ -2912,6 +2938,8 @@ describe('validation logic', () => { 'Argument of [date_extract] must be [chrono_literal], found value [booleanField] type [boolean]', 'Argument of [date_extract] must be [date], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_extract(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_extract(nullVar, nullVar)', []); }); describe('date_format', () => { @@ -2949,6 +2977,8 @@ describe('validation logic', () => { 'Argument of [date_format] must be [string], found value [booleanField] type [boolean]', 'Argument of [date_format] must be [date], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_format(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_format(nullVar, nullVar)', []); }); describe('date_parse', () => { @@ -2998,6 +3028,8 @@ describe('validation logic', () => { 'Argument of [date_parse] must be [string], found value [booleanField] type [boolean]', 'Argument of [date_parse] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval date_parse(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_parse(nullVar, nullVar)', []); }); describe('date_trunc', () => { @@ -3046,6 +3078,8 @@ describe('validation logic', () => { 'from a_index | eval var = date_trunc(to_datetime(dateField), to_datetime(dateField))', [] ); + testErrorsAndWarnings('from a_index | eval date_trunc(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval date_trunc(nullVar, nullVar)', []); }); describe('e', () => { @@ -3060,6 +3094,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort e()', []); + testErrorsAndWarnings('row nullVar = null | eval e()', []); }); describe('ends_with', () => { @@ -3106,6 +3141,8 @@ describe('validation logic', () => { 'Argument of [ends_with] must be [string], found value [booleanField] type [boolean]', 'Argument of [ends_with] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ends_with(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval ends_with(nullVar, nullVar)', []); }); describe('floor', () => { @@ -3155,6 +3192,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval floor(booleanField)', [ 'Argument of [floor] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval floor(null)', []); + testErrorsAndWarnings('row nullVar = null | eval floor(nullVar)', []); }); describe('greatest', () => { @@ -3293,6 +3332,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort greatest(booleanField)', []); + testErrorsAndWarnings('from a_index | eval greatest(null)', []); + testErrorsAndWarnings('row nullVar = null | eval greatest(nullVar)', []); }); describe('least', () => { @@ -3431,6 +3472,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort least(booleanField)', []); + testErrorsAndWarnings('from a_index | eval least(null)', []); + testErrorsAndWarnings('row nullVar = null | eval least(nullVar)', []); }); describe('left', () => { @@ -3492,6 +3535,8 @@ describe('validation logic', () => { 'Argument of [left] must be [string], found value [booleanField] type [boolean]', 'Argument of [left] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval left(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval left(nullVar, nullVar)', []); }); describe('length', () => { @@ -3541,6 +3586,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval length(booleanField)', [ 'Argument of [length] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval length(null)', []); + testErrorsAndWarnings('row nullVar = null | eval length(nullVar)', []); }); describe('log', () => { @@ -3622,6 +3669,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort log(numberField)', []); + testErrorsAndWarnings('from a_index | eval log(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval log(nullVar, nullVar)', []); }); describe('log10', () => { @@ -3671,6 +3720,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval log10(booleanField)', [ 'Argument of [log10] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval log10(null)', []); + testErrorsAndWarnings('row nullVar = null | eval log10(nullVar)', []); }); describe('ltrim', () => { @@ -3720,6 +3771,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval ltrim(booleanField)', [ 'Argument of [ltrim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval ltrim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval ltrim(nullVar)', []); }); describe('mv_avg', () => { @@ -3769,6 +3822,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_avg(booleanField)', [ 'Argument of [mv_avg] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_avg(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_avg(nullVar)', []); }); describe('mv_concat', () => { @@ -3836,6 +3891,8 @@ describe('validation logic', () => { 'Argument of [mv_concat] must be [string], found value [booleanField] type [boolean]', 'Argument of [mv_concat] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_concat(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_concat(nullVar, nullVar)', []); }); describe('mv_count', () => { @@ -3941,6 +3998,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_count(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_count(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_count(nullVar)', []); }); describe('mv_dedupe', () => { @@ -4052,6 +4111,10 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_dedupe(numberField)', []); + testErrorsAndWarnings('row var = mv_dedupe(to_cartesianpoint("POINT (30 10)"))', []); + testErrorsAndWarnings('from a_index | eval mv_dedupe(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval mv_dedupe(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_dedupe(nullVar)', []); }); describe('mv_first', () => { @@ -4149,6 +4212,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_first(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_first(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_first(nullVar)', []); }); describe('mv_last', () => { @@ -4246,6 +4311,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_last(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_last(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_last(nullVar)', []); }); describe('mv_max', () => { @@ -4319,6 +4386,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_max(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_max(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_max(nullVar)', []); }); describe('mv_median', () => { @@ -4368,6 +4437,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_median(booleanField)', [ 'Argument of [mv_median] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_median(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_median(nullVar)', []); }); describe('mv_min', () => { @@ -4441,6 +4512,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_min(booleanField)', []); + testErrorsAndWarnings('from a_index | eval mv_min(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_min(nullVar)', []); }); describe('mv_slice', () => { @@ -4794,6 +4867,8 @@ describe('validation logic', () => { 'from a_index | sort mv_slice(booleanField, numberField, numberField)', [] ); + testErrorsAndWarnings('from a_index | eval mv_slice(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_slice(nullVar, nullVar, nullVar)', []); }); describe('mv_sort', () => { @@ -4854,6 +4929,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort mv_sort(booleanField, "asc")', []); + testErrorsAndWarnings('from a_index | eval mv_sort(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_sort(nullVar, nullVar)', []); }); describe('mv_sum', () => { @@ -4903,6 +4980,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval mv_sum(booleanField)', [ 'Argument of [mv_sum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval mv_sum(null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_sum(nullVar)', []); }); describe('mv_zip', () => { @@ -5001,6 +5080,8 @@ describe('validation logic', () => { 'Argument of [mv_zip] must be [string], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval mv_zip(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval mv_zip(nullVar, nullVar, nullVar)', []); }); describe('now', () => { @@ -5014,6 +5095,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort now()', []); + testErrorsAndWarnings('row nullVar = null | eval now()', []); }); describe('pi', () => { @@ -5028,6 +5110,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort pi()', []); + testErrorsAndWarnings('row nullVar = null | eval pi()', []); }); describe('pow', () => { @@ -5086,6 +5169,8 @@ describe('validation logic', () => { 'Argument of [pow] must be [number], found value [booleanField] type [boolean]', 'Argument of [pow] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval pow(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval pow(nullVar, nullVar)', []); }); describe('replace', () => { @@ -5183,6 +5268,8 @@ describe('validation logic', () => { 'Argument of [replace] must be [string], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval replace(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval replace(nullVar, nullVar, nullVar)', []); }); describe('right', () => { @@ -5247,6 +5334,8 @@ describe('validation logic', () => { 'Argument of [right] must be [string], found value [booleanField] type [boolean]', 'Argument of [right] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval right(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval right(nullVar, nullVar)', []); }); describe('round', () => { @@ -5328,6 +5417,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort round(numberField)', []); + testErrorsAndWarnings('from a_index | eval round(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval round(nullVar, nullVar)', []); }); describe('rtrim', () => { @@ -5377,6 +5468,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval rtrim(booleanField)', [ 'Argument of [rtrim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval rtrim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval rtrim(nullVar)', []); }); describe('signum', () => { @@ -5426,6 +5519,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval signum(booleanField)', [ 'Argument of [signum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval signum(null)', []); + testErrorsAndWarnings('row nullVar = null | eval signum(nullVar)', []); }); describe('sin', () => { @@ -5475,6 +5570,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sin(booleanField)', [ 'Argument of [sin] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sin(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sin(nullVar)', []); }); describe('sinh', () => { @@ -5524,6 +5621,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sinh(booleanField)', [ 'Argument of [sinh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sinh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sinh(nullVar)', []); }); describe('split', () => { @@ -5588,6 +5687,8 @@ describe('validation logic', () => { 'Argument of [split] must be [string], found value [booleanField] type [boolean]', 'Argument of [split] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval split(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval split(nullVar, nullVar)', []); }); describe('sqrt', () => { @@ -5637,6 +5738,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval sqrt(booleanField)', [ 'Argument of [sqrt] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval sqrt(null)', []); + testErrorsAndWarnings('row nullVar = null | eval sqrt(nullVar)', []); }); describe('st_contains', () => { @@ -5987,6 +6090,8 @@ describe('validation logic', () => { 'from a_index | sort st_contains(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_contains(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_contains(nullVar, nullVar)', []); }); describe('st_disjoint', () => { @@ -6337,6 +6442,8 @@ describe('validation logic', () => { 'from a_index | sort st_disjoint(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_disjoint(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_disjoint(nullVar, nullVar)', []); }); describe('st_intersects', () => { @@ -6706,6 +6813,8 @@ describe('validation logic', () => { 'from a_index | sort st_intersects(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_intersects(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_intersects(nullVar, nullVar)', []); }); describe('st_within', () => { @@ -7056,6 +7165,8 @@ describe('validation logic', () => { 'from a_index | sort st_within(cartesianPointField, cartesianPointField)', [] ); + testErrorsAndWarnings('from a_index | eval st_within(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_within(nullVar, nullVar)', []); }); describe('st_x', () => { @@ -7118,6 +7229,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval var = st_x(to_geopoint(geoPointField))', []); testErrorsAndWarnings('from a_index | sort st_x(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_x(null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_x(nullVar)', []); }); describe('st_y', () => { @@ -7180,6 +7293,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval var = st_y(to_geopoint(geoPointField))', []); testErrorsAndWarnings('from a_index | sort st_y(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval st_y(null)', []); + testErrorsAndWarnings('row nullVar = null | eval st_y(nullVar)', []); }); describe('starts_with', () => { @@ -7230,6 +7345,8 @@ describe('validation logic', () => { 'Argument of [starts_with] must be [string], found value [booleanField] type [boolean]', 'Argument of [starts_with] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval starts_with(null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval starts_with(nullVar, nullVar)', []); }); describe('substring', () => { @@ -7331,6 +7448,8 @@ describe('validation logic', () => { 'Argument of [substring] must be [number], found value [booleanField] type [boolean]', ] ); + testErrorsAndWarnings('from a_index | eval substring(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval substring(nullVar, nullVar, nullVar)', []); }); describe('tan', () => { @@ -7380,6 +7499,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval tan(booleanField)', [ 'Argument of [tan] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval tan(null)', []); + testErrorsAndWarnings('row nullVar = null | eval tan(nullVar)', []); }); describe('tanh', () => { @@ -7429,6 +7550,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval tanh(booleanField)', [ 'Argument of [tanh] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval tanh(null)', []); + testErrorsAndWarnings('row nullVar = null | eval tanh(nullVar)', []); }); describe('tau', () => { @@ -7443,6 +7566,7 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort tau()', []); + testErrorsAndWarnings('row nullVar = null | eval tau()', []); }); describe('to_boolean', () => { @@ -7492,6 +7616,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_boolean(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_boolean(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_boolean(nullVar)', []); }); describe('to_cartesianpoint', () => { @@ -7548,6 +7674,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort to_cartesianpoint(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval to_cartesianpoint(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_cartesianpoint(nullVar)', []); }); describe('to_cartesianshape', () => { @@ -7626,6 +7754,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort to_cartesianshape(cartesianPointField)', []); + testErrorsAndWarnings('from a_index | eval to_cartesianshape(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_cartesianshape(nullVar)', []); }); describe('to_datetime', () => { @@ -7678,6 +7808,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_datetime(dateField)', []); + testErrorsAndWarnings('from a_index | eval to_datetime(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_datetime(nullVar)', []); }); describe('to_degrees', () => { @@ -7727,6 +7859,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_degrees(booleanField)', [ 'Argument of [to_degrees] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_degrees(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_degrees(nullVar)', []); }); describe('to_double', () => { @@ -7793,6 +7927,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_double(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_double(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_double(nullVar)', []); }); describe('to_geopoint', () => { @@ -7836,6 +7972,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_geopoint(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval to_geopoint(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_geopoint(nullVar)', []); }); describe('to_geoshape', () => { @@ -7891,6 +8029,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_geoshape(geoPointField)', []); + testErrorsAndWarnings('from a_index | eval to_geoshape(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_geoshape(nullVar)', []); }); describe('to_integer', () => { @@ -7957,6 +8097,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_integer(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_integer(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_integer(nullVar)', []); }); describe('to_ip', () => { @@ -7994,6 +8136,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_ip(ipField)', []); + testErrorsAndWarnings('from a_index | eval to_ip(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_ip(nullVar)', []); }); describe('to_long', () => { @@ -8052,6 +8196,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_long(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_long(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_long(nullVar)', []); }); describe('to_lower', () => { @@ -8101,6 +8247,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_lower(booleanField)', [ 'Argument of [to_lower] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_lower(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_lower(nullVar)', []); }); describe('to_radians', () => { @@ -8150,6 +8298,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_radians(booleanField)', [ 'Argument of [to_radians] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_radians(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_radians(nullVar)', []); }); describe('to_string', () => { @@ -8287,6 +8437,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_string(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_string(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_string(nullVar)', []); }); describe('to_unsigned_long', () => { @@ -8373,6 +8525,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_unsigned_long(booleanField)', []); + testErrorsAndWarnings('from a_index | eval to_unsigned_long(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_unsigned_long(nullVar)', []); }); describe('to_upper', () => { @@ -8422,6 +8576,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_upper(booleanField)', [ 'Argument of [to_upper] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval to_upper(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_upper(nullVar)', []); }); describe('to_version', () => { @@ -8452,6 +8608,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval to_version(stringField, extraArg)', [ 'Error: [to_version] function expects exactly one argument, got 2.', ]); + testErrorsAndWarnings('from a_index | eval to_version(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_version(nullVar)', []); }); describe('trim', () => { @@ -8501,6 +8659,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval trim(booleanField)', [ 'Argument of [trim] must be [string], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | eval trim(null)', []); + testErrorsAndWarnings('row nullVar = null | eval trim(nullVar)', []); }); describe('avg', () => { @@ -8605,6 +8765,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats avg(booleanField)', [ 'Argument of [avg] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats avg(null)', []); + testErrorsAndWarnings('row nullVar = null | stats avg(nullVar)', []); }); describe('sum', () => { @@ -8709,6 +8871,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats sum(booleanField)', [ 'Argument of [sum] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats sum(null)', []); + testErrorsAndWarnings('row nullVar = null | stats sum(nullVar)', []); }); describe('median', () => { @@ -8819,6 +8983,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats median(booleanField)', [ 'Argument of [median] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats median(null)', []); + testErrorsAndWarnings('row nullVar = null | stats median(nullVar)', []); }); describe('median_absolute_deviation', () => { @@ -8964,6 +9130,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats median_absolute_deviation(booleanField)', [ 'Argument of [median_absolute_deviation] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats median_absolute_deviation(null)', []); + testErrorsAndWarnings('row nullVar = null | stats median_absolute_deviation(nullVar)', []); }); describe('percentile', () => { @@ -9083,6 +9251,10 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats percentile(booleanField, 5)', [ 'Argument of [percentile] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats percentile(null, null)', []); + testErrorsAndWarnings('row nullVar = null | stats percentile(nullVar, nullVar)', [ + 'Argument of [percentile] must be a constant, received [nullVar]', + ]); }); describe('max', () => { @@ -9221,6 +9393,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats max(booleanField)', [ 'Argument of [max] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats max(null)', []); + testErrorsAndWarnings('row nullVar = null | stats max(nullVar)', []); }); describe('min', () => { @@ -9359,6 +9533,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats min(booleanField)', [ 'Argument of [min] must be [number], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats min(null)', []); + testErrorsAndWarnings('row nullVar = null | stats min(nullVar)', []); }); describe('count', () => { @@ -9404,6 +9580,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval count(stringField) > 0', [ 'EVAL does not support function count', ]); + testErrorsAndWarnings('from a_index | stats count(null)', []); + testErrorsAndWarnings('row nullVar = null | stats count(nullVar)', []); }); describe('count_distinct', () => { @@ -9462,6 +9640,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval count_distinct(stringField, numberField) > 0', [ 'EVAL does not support function count_distinct', ]); + testErrorsAndWarnings('from a_index | stats count_distinct(null, null)', []); + testErrorsAndWarnings('row nullVar = null | stats count_distinct(nullVar, nullVar)', []); }); describe('st_centroid_agg', () => { @@ -9546,6 +9726,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | stats st_centroid_agg(booleanField)', [ 'Argument of [st_centroid_agg] must be [cartesian_point], found value [booleanField] type [boolean]', ]); + testErrorsAndWarnings('from a_index | stats st_centroid_agg(null)', []); + testErrorsAndWarnings('row nullVar = null | stats st_centroid_agg(nullVar)', []); }); describe('values', () => { @@ -9579,6 +9761,8 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | eval values(stringField) > 0', [ 'EVAL does not support function values', ]); + testErrorsAndWarnings('from a_index | stats values(null)', []); + testErrorsAndWarnings('row nullVar = null | stats values(nullVar)', []); }); describe('bucket', () => { @@ -9655,6 +9839,16 @@ describe('validation logic', () => { testErrorsAndWarnings('from a_index | sort bucket(dateField, 1 year)', [ 'SORT does not support function bucket', ]); + testErrorsAndWarnings('from a_index | stats bucket(null, null, null, null)', []); + + testErrorsAndWarnings( + 'row nullVar = null | stats bucket(nullVar, nullVar, nullVar, nullVar)', + [ + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + 'Argument of [bucket] must be a constant, received [nullVar]', + ] + ); }); describe('cbrt', () => { @@ -9689,6 +9883,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort cbrt(numberField)', []); + testErrorsAndWarnings('from a_index | eval cbrt(null)', []); + testErrorsAndWarnings('row nullVar = null | eval cbrt(nullVar)', []); }); describe('from_base64', () => { @@ -9723,6 +9919,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort from_base64(stringField)', []); + testErrorsAndWarnings('from a_index | eval from_base64(null)', []); + testErrorsAndWarnings('row nullVar = null | eval from_base64(nullVar)', []); }); describe('locate', () => { @@ -9806,6 +10004,8 @@ describe('validation logic', () => { ); testErrorsAndWarnings('from a_index | sort locate(stringField, stringField)', []); + testErrorsAndWarnings('from a_index | eval locate(null, null, null)', []); + testErrorsAndWarnings('row nullVar = null | eval locate(nullVar, nullVar, nullVar)', []); }); describe('to_base64', () => { @@ -9840,6 +10040,8 @@ describe('validation logic', () => { ]); testErrorsAndWarnings('from a_index | sort to_base64(stringField)', []); + testErrorsAndWarnings('from a_index | eval to_base64(null)', []); + testErrorsAndWarnings('row nullVar = null | eval to_base64(nullVar)', []); }); }); }); From 5bf276d21f7ca8fd42d10a2c3b9679e5b6c4027b Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Tue, 28 May 2024 16:03:05 +0200 Subject: [PATCH 36/59] [Security Solution] Create `RulesManagementClient`, initial implementation (#182802) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Partially addresses: https://github.com/elastic/kibana/issues/180128** ## Summary - Creates `RulesManagementClient`, which centralizes CRUD utilites for rules, at `x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/rules_management_client.ts` - Move ML Auth validation from endpoints to the new client utils. - Adds client to `SecuritySolution` API requests context. - Deletes `createRules`, `deletesRules`,`updateRules` and `patchRules` utils and replaces them with new client methods. - **Testing**: - Creates individual test files for each "public" method of the RulesManagementClient. - Creates importable mock of the client ## To-Do: - Replace `readRules` method for a new public method within the API (left out of this PR to keep scope and size manageable) ## Flaky Test Runner - [FTR Exceptions - ESS](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6080) 🟢 - [FTR Exceptions - Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6081) 🟢 - [FTR - Prebuilt Rules - Bundled Prebuilt Rules Package - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6083) 🟢 - [FTR - Prebuilt Rules - Large Prebuilt Rules Package - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6084) 🟢 - [FTR - Prebuilt Rules - Management - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6085) 🟢 - [FTR - Prebuilt Rules - Update Prebuilt Rules Package - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6085) 🟢 - [FTR - Rules Management - Rule Bulk Actions - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6087) 🟢 - [FTR - Rules Management - Rule Creation - Basic License Essentials Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6088) 🟢 - [FTR - Rules Management - Rule Creation - Trial License Complete Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6089) 🟢 - [FTR - Rules Management - Rule Deletion - Basic License Essentials Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6090) (name is wrong in the FTR UI, but runs the correct tests) 🟢 - [FTR - Rules Management - Rule Deletion - Trial License Complete Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6091) 🟢 - [FTR - Rules Management - Rule Import and Export - Basic License Essentials Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6092) 🟢 - [FTR - Rules Management - Rule Import and Export - Trial License Complete Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6093) 🟢 - [FTR - Rules Management - Rule Patch - Basic License Essentials Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6095) 🟢 - [FTR - Rules Management - Rule Patch - Trial License Complete Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6096) 🟢 - [FTR - Rules Management - Rule Update - Basic License Essentials Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6097) 🟢 - [FTR - Rules Management - Rule Update - Trial License Complete Tier - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6098) 🟢 - [Cypress - Security Solution Rule Management - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6099) 🟢 - [Cypress - Security Solution Rule Management - Prebuilt Rules - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6100) 🟢 - [Cypress - Security Solution Detection Engine - Exceptions - ESS and Serverless](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/6101) 🟢 ### Checklist Delete any items that are not applicable to this PR. - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Dmitrii Shevchenko --- ...tall_prebuilt_rules_and_timelines_route.ts | 5 +- .../perform_rule_installation_route.ts | 3 +- .../perform_rule_upgrade_route.ts | 3 +- .../rule_objects/create_prebuilt_rules.ts | 18 +- .../upgrade_prebuilt_rules.test.ts | 240 ---------- .../rule_objects/upgrade_prebuilt_rules.ts | 89 +--- .../routes/__mocks__/request_context.ts | 3 + .../api/create_rule_exceptions/route.ts | 25 +- .../rule_management/api/register_routes.ts | 14 +- .../fetch_rules_by_query_or_ids.ts | 2 +- .../api/rules/bulk_actions/route.test.ts | 4 +- .../api/rules/bulk_actions/route.ts | 5 +- .../api/rules/bulk_create_rules/route.test.ts | 21 +- .../api/rules/bulk_create_rules/route.ts | 27 +- .../api/rules/bulk_delete_rules/route.ts | 7 +- .../api/rules/bulk_patch_rules/route.test.ts | 56 ++- .../api/rules/bulk_patch_rules/route.ts | 44 +- .../api/rules/bulk_update_rules/route.test.ts | 20 +- .../api/rules/bulk_update_rules/route.ts | 39 +- .../api/rules/create_rule/route.test.ts | 20 +- .../api/rules/create_rule/route.ts | 24 +- .../api/rules/delete_rule/route.test.ts | 3 +- .../api/rules/delete_rule/route.ts | 7 +- .../api/rules/import_rules/route.test.ts | 24 +- .../api/rules/import_rules/route.ts | 20 +- .../api/rules/patch_rule/route.test.ts | 60 ++- .../api/rules/patch_rule/route.ts | 50 +- .../api/rules/read_rule/route.ts | 2 +- .../api/rules/update_rule/route.test.ts | 17 +- .../api/rules/update_rule/route.ts | 45 +- .../logic/crud/create_rules.test.ts | 82 ---- .../logic/crud/create_rules.ts | 42 -- .../logic/crud/delete_rules.ts | 18 - .../rule_management/logic/crud/patch_rules.ts | 56 --- .../logic/crud/update_rules.mock.ts | 26 -- .../logic/crud/update_rules.test.ts | 81 ---- .../logic/crud/update_rules.ts | 97 ---- .../logic/import/import_rules_utils.test.ts | 202 +------- .../logic/import/import_rules_utils.ts | 74 +-- .../rule_management/__mocks__/read_rules.ts | 21 + .../__mocks__/rules_management_client.ts | 74 +++ ...custom_rule.rule_management_client.test.ts | 155 +++++++ ...ebuilt_rule.rule_management_client.test.ts | 172 +++++++ ...elete_rule.rule_management_client.test.ts} | 19 +- ...import_rule.rule_management_client.test.ts | 185 ++++++++ ...patch_rule.rule_management_client.test.ts} | 153 +++++-- .../read_rules.test.ts | 0 .../{crud => rule_management}/read_rules.ts | 0 .../rules_management_client.ts | 430 ++++++++++++++++++ ...update_rule.rule_management_client.test.ts | 236 ++++++++++ ...ebuilt_rule.rule_management_client.test.ts | 170 +++++++ .../normalization/rule_converters.ts | 80 +++- .../rule_objects/fetch_rule_by_id.ts | 2 +- .../machine_learning/__mocks__/validation.ts | 9 + .../server/request_context_factory.ts | 11 + .../plugins/security_solution/server/types.ts | 2 + 56 files changed, 1921 insertions(+), 1373 deletions(-) delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts rename x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/{crud/delete_rules.test.ts => rule_management/delete_rule.rule_management_client.test.ts} (52%) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts rename x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/{crud/patch_rules.test.ts => rule_management/patch_rule.rule_management_client.test.ts} (64%) rename x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/{crud => rule_management}/read_rules.test.ts (100%) rename x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/{crud => rule_management}/read_rules.ts (100%) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts index 20f1bc962232e..8140f78e285ab 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route.ts @@ -85,6 +85,7 @@ export const createPrepackagedRules = async ( const savedObjectsClient = context.core.savedObjects.client; const siemClient = context.getAppClient(); const exceptionsListClient = context.getExceptionListClient() ?? exceptionsClient; + const rulesManagementClient = context.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(savedObjectsClient); if (!siemClient || !rulesClient) { @@ -106,14 +107,14 @@ export const createPrepackagedRules = async ( const rulesToInstall = getRulesToInstall(latestPrebuiltRules, installedPrebuiltRules); const rulesToUpdate = getRulesToUpdate(latestPrebuiltRules, installedPrebuiltRules); - const result = await createPrebuiltRules(rulesClient, rulesToInstall); + const result = await createPrebuiltRules(rulesManagementClient, rulesToInstall); if (result.errors.length > 0) { throw new AggregateError(result.errors, 'Error installing new prebuilt rules'); } const { result: timelinesResult } = await performTimelinesInstallation(context); - await upgradePrebuiltRules(rulesClient, rulesToUpdate); + await upgradePrebuiltRules(rulesManagementClient, rulesToUpdate); const prebuiltRulesOutput: InstallPrebuiltRulesAndTimelinesResponse = { rules_installed: rulesToInstall.length, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts index d76c6d93fc852..61e928a92a39d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_installation/perform_rule_installation_route.ts @@ -59,6 +59,7 @@ export const performRuleInstallationRoute = (router: SecuritySolutionPluginRoute const config = ctx.securitySolution.getConfig(); const soClient = ctx.core.savedObjects.client; const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); const exceptionsListClient = ctx.securitySolution.getExceptionListClient(); @@ -109,7 +110,7 @@ export const performRuleInstallationRoute = (router: SecuritySolutionPluginRoute } const { results: installedRules, errors: installationErrors } = await createPrebuiltRules( - rulesClient, + rulesManagementClient, installableRules ); const ruleErrors = [...fetchErrors, ...installationErrors]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts index 09342a9150a50..22951de09689c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/perform_rule_upgrade/perform_rule_upgrade_route.ts @@ -60,6 +60,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => const ctx = await context.resolve(['core', 'alerting', 'securitySolution']); const soClient = ctx.core.savedObjects.client; const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleAssetsClient = createPrebuiltRuleAssetsClient(soClient); const ruleObjectsClient = createPrebuiltRuleObjectsClient(rulesClient); @@ -156,7 +157,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) => // Perform the upgrade const { results: updatedRules, errors: installationErrors } = await upgradePrebuiltRules( - rulesClient, + rulesManagementClient, targetRules ); const ruleErrors = [...fetchErrors, ...installationErrors]; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts index 54b4361bf868b..9f65149e7c46e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/create_prebuilt_rules.ts @@ -5,27 +5,27 @@ * 2.0. */ -import type { RulesClient } from '@kbn/alerting-plugin/server'; import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; import { initPromisePool } from '../../../../../utils/promise_pool'; import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; -export const createPrebuiltRules = (rulesClient: RulesClient, rules: PrebuiltRuleAsset[]) => - withSecuritySpan('createPrebuiltRules', async () => { +export const createPrebuiltRules = ( + rulesManagementClient: IRulesManagementClient, + rules: PrebuiltRuleAsset[] +) => { + return withSecuritySpan('createPrebuiltRules', async () => { const result = await initPromisePool({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, items: rules, executor: async (rule) => { - return createRules({ - rulesClient, - params: rule, - immutable: true, - defaultEnabled: false, + return rulesManagementClient.createPrebuiltRule({ + ruleAsset: rule, }); }, }); return result; }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts deleted file mode 100644 index f4845be99c68e..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.test.ts +++ /dev/null @@ -1,240 +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 { omit } from 'lodash'; -import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { - getRuleMock, - getFindResultWithSingleHit, - getFindResultWithMultiHits, -} from '../../../routes/__mocks__/request_responses'; -import { upgradePrebuiltRules } from './upgrade_prebuilt_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; -import { deleteRules } from '../../../rule_management/logic/crud/delete_rules'; -import { getPrebuiltRuleMock, getPrebuiltThreatMatchRuleMock } from '../../mocks'; -import { getQueryRuleParams, getThreatRuleParams } from '../../../rule_schema/mocks'; - -jest.mock('../../../rule_management/logic/crud/patch_rules'); -jest.mock('../../../rule_management/logic/crud/create_rules'); -jest.mock('../../../rule_management/logic/crud/delete_rules'); - -describe('updatePrebuiltRules', () => { - let rulesClient: ReturnType; - - beforeEach(() => { - rulesClient = rulesClientMock.create(); - }); - - describe('when upgrading a prebuilt rule to a newer version with the same rule type', () => { - const prepackagedRule = getPrebuiltRuleMock({ - rule_id: 'rule-to-upgrade', - }); - - beforeEach(() => { - const installedRule = getRuleMock( - getQueryRuleParams({ - ruleId: 'rule-to-upgrade', - }) - ); - - rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ - data: [installedRule], - }) - ); - }); - - it('patches existing rule with incoming version data', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining(prepackagedRule), - }) - ); - }); - - it('makes sure enabled state is preserved', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - enabled: undefined, - }), - }) - ); - }); - }); - - describe('when upgrading a prebuilt rule to a newer version with a different rule type', () => { - const prepackagedRule = getPrebuiltRuleMock({ - rule_id: 'rule-to-upgrade', - type: 'eql', - language: 'eql', - query: 'host where host.name == "something"', - }); - const actions = [ - { - group: 'group', - id: 'id', - actionTypeId: 'action_type_id', - params: {}, - }, - ]; - const installedRule = getRuleMock( - getQueryRuleParams({ - ruleId: 'rule-to-upgrade', - type: 'query', - exceptionsList: [ - { - id: 'exception_list_1', - list_id: 'exception_list_1', - namespace_type: 'agnostic', - type: 'rule_default', - }, - ], - timelineId: 'some-timeline-id', - timelineTitle: 'Some timeline title', - }), - { - id: 'installed-rule-so-id', - actions, - } - ); - - beforeEach(() => { - rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ - data: [installedRule], - }) - ); - }); - - it('deletes rule before creation', async () => { - let lastCalled!: string; - - (deleteRules as jest.Mock).mockImplementation(() => (lastCalled = 'deleteRules')); - (createRules as jest.Mock).mockImplementation(() => (lastCalled = 'createRules')); - - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(deleteRules).toHaveBeenCalledTimes(1); - expect(createRules).toHaveBeenCalledTimes(1); - expect(lastCalled).toBe('createRules'); - }); - - it('recreates a rule with incoming version data', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - immutable: true, - params: expect.objectContaining(prepackagedRule), - }) - ); - }); - - it('restores saved object id', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - id: 'installed-rule-so-id', - }) - ); - }); - - it('restores enabled state', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ enabled: installedRule.enabled }), - }) - ); - }); - - it('restores actions', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - actions: actions.map((a) => ({ - ...omit(a, 'actionTypeId'), - action_type_id: a.actionTypeId, - })), - }), - }) - ); - }); - - it('restores exceptions list', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ exceptions_list: installedRule.params.exceptionsList }), - }) - ); - }); - - it('restores timeline reference', async () => { - await upgradePrebuiltRules(rulesClient, [prepackagedRule]); - - expect(createRules).toHaveBeenCalledWith( - expect.objectContaining({ - params: expect.objectContaining({ - timeline_id: installedRule.params.timelineId, - timeline_title: installedRule.params.timelineTitle, - }), - }) - ); - }); - }); - - it('should update threat match rules', async () => { - const updatedThreatParams = { - threat_index: ['test-index'], - threat_indicator_path: 'test.path', - threat_query: 'threat:*', - }; - const prepackagedRule = getPrebuiltThreatMatchRuleMock(); - rulesClient.find.mockResolvedValue({ - ...getFindResultWithSingleHit(), - data: [getRuleMock(getThreatRuleParams())], - }); - - await upgradePrebuiltRules(rulesClient, [{ ...prepackagedRule, ...updatedThreatParams }]); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_indicator_path: 'test.path', - }), - }) - ); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_index: ['test-index'], - }), - }) - ); - - expect(patchRules).toHaveBeenCalledWith( - expect.objectContaining({ - nextParams: expect.objectContaining({ - threat_query: 'threat:*', - }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts index 98afa86114e4f..a6ff9b0dea9c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/rule_objects/upgrade_prebuilt_rules.ts @@ -5,106 +5,31 @@ * 2.0. */ -import type { SanitizedRule } from '@kbn/alerting-plugin/common'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../common/constants'; -import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; import { initPromisePool } from '../../../../../utils/promise_pool'; import { withSecuritySpan } from '../../../../../utils/with_security_span'; -import { createRules } from '../../../rule_management/logic/crud/create_rules'; -import { deleteRules } from '../../../rule_management/logic/crud/delete_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; -import { readRules } from '../../../rule_management/logic/crud/read_rules'; -import type { RuleParams } from '../../../rule_schema'; -import { PrepackagedRulesError } from '../../api/install_prebuilt_rules_and_timelines/install_prebuilt_rules_and_timelines_route'; import type { PrebuiltRuleAsset } from '../../model/rule_assets/prebuilt_rule_asset'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; /** * Upgrades existing prebuilt rules given a set of rules and output index. * This implements a chunked approach to not saturate network connections and * avoid being a "noisy neighbor". - * @param rulesClient Alerting client + * @param rulesManagementClient RulesManagementClient * @param rules The rules to apply the update for */ -export const upgradePrebuiltRules = async (rulesClient: RulesClient, rules: PrebuiltRuleAsset[]) => +export const upgradePrebuiltRules = async ( + rulesManagementClient: IRulesManagementClient, + rules: PrebuiltRuleAsset[] +) => withSecuritySpan('upgradePrebuiltRules', async () => { const result = await initPromisePool({ concurrency: MAX_RULES_TO_UPDATE_IN_PARALLEL, items: rules, executor: async (rule) => { - return upgradeRule(rulesClient, rule); + return rulesManagementClient.upgradePrebuiltRule({ ruleAsset: rule }); }, }); return result; }); - -/** - * Upgrades a rule - * - * @param rulesClient Alerting client - * @param rule The rule to apply the update for - * @returns Promise of what was updated. - */ -const upgradeRule = async ( - rulesClient: RulesClient, - rule: PrebuiltRuleAsset -): Promise> => { - const existingRule = await readRules({ - rulesClient, - ruleId: rule.rule_id, - id: undefined, - }); - - if (!existingRule) { - throw new PrepackagedRulesError(`Failed to find rule ${rule.rule_id}`, 500); - } - - // If we're trying to change the type of a prepackaged rule, we need to delete the old one - // and replace it with the new rule, keeping the enabled setting, actions, throttle, id, - // and exception lists from the old rule - if (rule.type !== existingRule.params.type) { - await deleteRules({ - ruleId: existingRule.id, - rulesClient, - }); - - return createRules({ - rulesClient, - immutable: true, - id: existingRule.id, - params: { - ...rule, - // Force the prepackaged rule to use the enabled state from the existing rule, - // regardless of what the prepackaged rule says - enabled: existingRule.enabled, - exceptions_list: existingRule.params.exceptionsList, - actions: existingRule.actions.map(transformAlertToRuleAction), - timeline_id: existingRule.params.timelineId, - timeline_title: existingRule.params.timelineTitle, - }, - }); - } - - await patchRules({ - rulesClient, - existingRule, - nextParams: { - ...rule, - // Force enabled to use the enabled state from the existing rule by passing in undefined to patchRules - enabled: undefined, - }, - }); - - const updatedRule = await readRules({ - rulesClient, - ruleId: rule.rule_id, - id: undefined, - }); - - if (!updatedRule) { - throw new PrepackagedRulesError(`Rule ${rule.rule_id} not found after upgrade`, 500); - } - - return updatedRule; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts index 0893c7297763c..ebc18daba1c9f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_context.ts @@ -38,6 +38,7 @@ import { riskEngineDataClientMock } from '../../../entity_analytics/risk_engine/ import { riskScoreDataClientMock } from '../../../entity_analytics/risk_score/risk_score_data_client.mock'; import { assetCriticalityDataClientMock } from '../../../entity_analytics/asset_criticality/asset_criticality_data_client.mock'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; +import { rulesManagementClientMock } from '../../rule_management/logic/rule_management/__mocks__/rules_management_client'; export const createMockClients = () => { const core = coreMock.createRequestHandlerContext(); @@ -57,6 +58,7 @@ export const createMockClients = () => { exceptionListClient: listMock.getExceptionListClient(core.savedObjects.client), }, rulesClient: rulesClientMock.create(), + rulesManagementClient: rulesManagementClientMock.create(), actionsClient: actionsClientMock.create(), ruleDataService: ruleRegistryMocks.createRuleDataService(), @@ -140,6 +142,7 @@ const createSecuritySolutionRequestContextMock = ( }), getSpaceId: jest.fn(() => 'default'), getRuleDataService: jest.fn(() => clients.ruleDataService), + getRulesManagementClient: jest.fn(() => clients.rulesManagementClient), getDetectionEngineHealthClient: jest.fn(() => clients.detectionEngineHealthClient), getRuleExecutionLog: jest.fn(() => clients.ruleExecutionLog), getExceptionListClient: jest.fn(() => clients.lists.exceptionListClient), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts index 5f5b29abdf38e..0334ded224f43 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_exceptions/api/create_rule_exceptions/route.ts @@ -26,7 +26,6 @@ import { import { formatErrors, validate } from '@kbn/securitysolution-io-ts-utils'; import type { SanitizedRule } from '@kbn/alerting-plugin/common'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; import type { CreateRuleExceptionsRequestBodyDecoded, @@ -38,13 +37,13 @@ import { CreateRuleExceptionsRequestParams, } from '../../../../../../common/api/detection_engine/rule_exceptions'; -import { readRules } from '../../../rule_management/logic/crud/read_rules'; -import { patchRules } from '../../../rule_management/logic/crud/patch_rules'; +import { readRules } from '../../../rule_management/logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../rule_management/logic/exceptions/check_for_default_rule_exception_list'; import type { RuleParams } from '../../../rule_schema'; import type { SecuritySolutionPluginRouter } from '../../../../../types'; import { buildSiemResponse } from '../../../routes/utils'; import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation'; +import type { IRulesManagementClient } from '../../../rule_management/logic/rule_management/rules_management_client'; export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) => { router.versioned @@ -84,6 +83,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) ]); const rulesClient = ctx.alerting.getRulesClient(); const listsClient = ctx.securitySolution.getExceptionListClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const { items } = request.body; const { id: ruleId } = request.params; @@ -106,7 +106,7 @@ export const createRuleExceptionsRoute = (router: SecuritySolutionPluginRouter) items, rule, listsClient, - rulesClient, + rulesManagementClient, }); const [validated, errors] = validate(createdItems, t.array(exceptionListItemSchema)); @@ -130,11 +130,11 @@ export const createRuleExceptions = async ({ items, rule, listsClient, - rulesClient, + rulesManagementClient, }: { items: CreateRuleExceptionListItemSchemaDecoded[]; listsClient: ExceptionListClient | null; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; rule: SanitizedRule; }) => { const ruleDefaultLists = rule.params.exceptionsList.filter( @@ -168,8 +168,8 @@ export const createRuleExceptions = async ({ // and update the rule's exceptions lists to include newly created default list. const defaultList = await createAndAssociateDefaultExceptionList({ rule, - rulesClient, listsClient, + rulesManagementClient, removeOldAssociation: true, }); @@ -178,8 +178,8 @@ export const createRuleExceptions = async ({ } else { const defaultList = await createAndAssociateDefaultExceptionList({ rule, - rulesClient, listsClient, + rulesManagementClient, removeOldAssociation: false, }); @@ -272,12 +272,12 @@ export const createExceptionList = async ({ export const createAndAssociateDefaultExceptionList = async ({ rule, listsClient, - rulesClient, + rulesManagementClient, removeOldAssociation, }: { rule: SanitizedRule; listsClient: ExceptionListClient | null; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; removeOldAssociation: boolean; }): Promise => { const exceptionListToAssociate = await createExceptionList({ rule, listsClient }); @@ -294,10 +294,9 @@ export const createAndAssociateDefaultExceptionList = async ({ ? existingRuleExceptionLists.filter((list) => list.type !== ExceptionListTypeEnum.RULE_DEFAULT) : existingRuleExceptionLists; - await patchRules({ - rulesClient, - existingRule: rule, + await rulesManagementClient.patchRule({ nextParams: { + rule_id: rule.params.ruleId, ...rule.params, exceptions_list: [ ...ruleExceptionLists, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts index 75414a8cd08ef..e6999cc9e429e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/register_routes.ts @@ -34,16 +34,16 @@ export const registerRuleManagementRoutes = ( logger: Logger ) => { // Rules CRUD - createRuleRoute(router, ml); + createRuleRoute(router); readRuleRoute(router, logger); - updateRuleRoute(router, ml); - patchRuleRoute(router, ml); + updateRuleRoute(router); + patchRuleRoute(router); deleteRuleRoute(router); // Rules bulk CRUD - bulkCreateRulesRoute(router, ml, logger); - bulkUpdateRulesRoute(router, ml, logger); - bulkPatchRulesRoute(router, ml, logger); + bulkCreateRulesRoute(router, logger); + bulkUpdateRulesRoute(router, logger); + bulkPatchRulesRoute(router, logger); bulkDeleteRulesRoute(router, logger); // Rules bulk actions @@ -51,7 +51,7 @@ export const registerRuleManagementRoutes = ( // Rules export/import exportRulesRoute(router, config, logger); - importRulesRoute(router, config, ml); + importRulesRoute(router, config); // Rules search findRulesRoute(router, logger); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts index 2a07ecc95509b..1ef231bb0b5ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/fetch_rules_by_query_or_ids.ts @@ -11,7 +11,7 @@ import { MAX_RULES_TO_UPDATE_IN_PARALLEL } from '../../../../../../../common/con import type { PromisePoolOutcome } from '../../../../../../utils/promise_pool'; import { initPromisePool } from '../../../../../../utils/promise_pool'; import type { RuleAlertType } from '../../../../rule_schema'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { findRules } from '../../../logic/search/find_rules'; import { MAX_RULES_TO_PROCESS_TOTAL } from './route'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts index 0b7710db26425..d74afe0bea65e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.test.ts @@ -30,10 +30,10 @@ import { getBulkDisableRuleActionSchemaMock, } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; jest.mock('../../../../../machine_learning/authz'); -jest.mock('../../../logic/crud/read_rules', () => ({ readRules: jest.fn() })); +jest.mock('../../../logic/rule_management/read_rules', () => ({ readRules: jest.fn() })); describe('Perform bulk action route', () => { const readRulesMock = readRules as jest.Mock; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts index ae0298ada97c1..f5932285a8e33 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_actions/route.ts @@ -35,7 +35,6 @@ import { dryRunValidateBulkEditRule, validateBulkDuplicateRule, } from '../../../logic/bulk_actions/validations'; -import { deleteRules } from '../../../logic/crud/delete_rules'; import { getExportByObjectIds } from '../../../logic/export/get_export_by_object_ids'; import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; import type { BulkActionError } from './bulk_actions_response'; @@ -121,6 +120,7 @@ export const performBulkActionRoute = ( const exceptionsClient = ctx.lists?.getExceptionListClient(); const savedObjectsClient = ctx.core.savedObjects.client; const actionsClient = ctx.actions.getActionsClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const { getExporter, getClient } = ctx.core.savedObjects; const client = getClient({ includedHiddenTypes: ['action'] }); @@ -203,9 +203,8 @@ export const performBulkActionRoute = ( return null; } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); return null; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts index 59e875a261677..8c73c4334dde6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.test.ts @@ -6,8 +6,6 @@ */ import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getReadBulkRequest, getFindResultWithSingleHit, @@ -23,27 +21,26 @@ import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detect import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk create rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful creation - + clients.rulesManagementClient.createCustomRule.mockResolvedValue( + getRuleMock(getQueryRuleParams()) + ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - bulkCreateRulesRoute(server.router, ml, logger); + bulkCreateRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -58,10 +55,8 @@ describe('Bulk create rules route', () => { describe('unhappy paths', () => { test('returns a 403 error object if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.createCustomRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject( @@ -113,7 +108,7 @@ describe('Bulk create rules route', () => { }); test('catches error if creation throws', async () => { - clients.rulesClient.create.mockImplementation(async () => { + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts index abd14b7fbb4a9..ee9be34e14e98 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_create_rules/route.ts @@ -16,11 +16,7 @@ import { } from '../../../../../../../common/api/detection_engine/rule_management'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; -import { createRules } from '../../../logic/crud/create_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDuplicates } from './get_duplicates'; import { transformValidateBulkError } from '../../../utils/validate'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; @@ -37,11 +33,7 @@ import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../. /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkCreateRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkCreateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .post({ access: 'public', @@ -69,16 +61,8 @@ export const bulkCreateRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'licensing', 'alerting']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const ruleDefinitions = request.body; const dupes = getDuplicates(ruleDefinitions, 'rule_id'); @@ -125,10 +109,7 @@ export const bulkCreateRulesRoute = ( }); } - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - - const createdRule = await createRules({ - rulesClient, + const createdRule = await rulesManagementClient.createCustomRule({ params: payloadRule, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts index 98c938f43a7bb..a8c3b3d96060e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_delete_rules/route.ts @@ -24,8 +24,7 @@ import { createBulkErrorObject, transformBulkError, } from '../../../../routes/utils'; -import { deleteRules } from '../../../logic/crud/delete_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; @@ -56,6 +55,7 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { @@ -76,9 +76,8 @@ export const bulkDeleteRulesRoute = (router: SecuritySolutionPluginRouter, logge return getIdBulkError({ id, ruleId }); } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); return transformValidateBulkError(idOrRuleIdOrUnknown, rule); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts index ca3cde890b738..b5644237d8f26 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.test.ts @@ -9,8 +9,6 @@ import { DETECTION_ENGINE_RULES_BULK_UPDATE, DETECTION_ENGINE_RULES_URL, } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getFindResultWithSingleHit, @@ -23,24 +21,22 @@ import { bulkPatchRulesRoute } from './route'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk patch rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds + clients.rulesManagementClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); - bulkPatchRulesRoute(server.router, ml, logger); + bulkPatchRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -68,11 +64,23 @@ describe('Bulk patch rules route', () => { }); test('allows ML Params to be patched', async () => { + const anomalyThreshold = 4; + const machineLearningJobId = 'some_job_id'; + clients.rulesClient.get.mockResolvedValueOnce(getRuleMock(getMlRuleParams())); clients.rulesClient.find.mockResolvedValueOnce({ ...getFindResultWithSingleHit(), data: [getRuleMock(getMlRuleParams())], }); + clients.rulesManagementClient.patchRule.mockResolvedValueOnce( + getRuleMock( + getMlRuleParams({ + anomalyThreshold, + machineLearningJobId: [machineLearningJobId], + }) + ) + ); + const request = requestMock.create({ method: 'patch', path: `${DETECTION_ENGINE_RULES_URL}/bulk_update`, @@ -80,31 +88,23 @@ describe('Bulk patch rules route', () => { { type: 'machine_learning', rule_id: 'my-rule-id', - anomaly_threshold: 4, - machine_learning_job_id: 'some_job_id', + anomaly_threshold: anomalyThreshold, + machine_learning_job_id: machineLearningJobId, }, ], }); - await server.inject(request, requestContextMock.convertContext(context)); - - expect(clients.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 4, - machineLearningJobId: ['some_job_id'], - }), - }), - }) - ); + const response = await server.inject(request, requestContextMock.convertContext(context)); + + expect(response.status).toEqual(200); + expect(response.body[0].machine_learning_job_id).toEqual([machineLearningJobId]); + expect(response.body[0].anomaly_threshold).toEqual(anomalyThreshold); }); it('rejects patching a rule to ML if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_BULK_UPDATE, @@ -125,10 +125,8 @@ describe('Bulk patch rules route', () => { }); it('rejects patching an existing ML rule if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const { type, ...payloadWithoutType } = typicalMlRulePayload(); const request = requestMock.create({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts index 5d6d74f230ce5..06840ed328ee5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_patch_rules/route.ts @@ -16,14 +16,10 @@ import { import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { transformBulkError, buildSiemResponse } from '../../../../routes/utils'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; -import { patchRules } from '../../../logic/crud/patch_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list'; @@ -32,11 +28,7 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkPatchRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkPatchRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .patch({ access: 'public', @@ -64,35 +56,22 @@ export const bulkPatchRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { const idOrRuleIdOrUnknown = payloadRule.id ?? payloadRule.rule_id ?? '(unknown id)'; try { - if (payloadRule.type) { - // reject an unauthorized "promotion" to ML - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - } - const existingRule = await readRules({ rulesClient, ruleId: payloadRule.rule_id, id: payloadRule.id, }); - if (existingRule?.params.type) { - // reject an unauthorized modification of an ML rule - throwAuthzError(await mlAuthz.validateRuleType(existingRule?.params.type)); + + if (!existingRule) { + return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); } validateRulesWithDuplicatedDefaultExceptionsList({ @@ -108,16 +87,11 @@ export const bulkPatchRulesRoute = ( ruleId: payloadRule.id, }); - const rule = await patchRules({ - existingRule, - rulesClient, + const rule = await rulesManagementClient.patchRule({ nextParams: payloadRule, }); - if (rule != null && rule.enabled != null && rule.name != null) { - return transformValidateBulkError(rule.id, rule); - } else { - return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); - } + + return transformValidateBulkError(rule.id, rule); } catch (err) { return transformBulkError(idOrRuleIdOrUnknown, err); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts index 98752160fab96..8f1b066c8b4ba 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.test.ts @@ -6,8 +6,6 @@ */ import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../../../common/constants'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getRuleMock, @@ -21,26 +19,23 @@ import type { BulkError } from '../../../../routes/utils'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { loggingSystemMock } from '@kbn/core/server/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Bulk update rules route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - + clients.rulesManagementClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - bulkUpdateRulesRoute(server.router, ml, logger); + bulkUpdateRulesRoute(server.router, logger); }); describe('status codes', () => { @@ -71,7 +66,7 @@ describe('Bulk update rules route', () => { }); test('returns an error if update throws', async () => { - clients.rulesClient.update.mockImplementation(() => { + clients.rulesManagementClient.updateRule.mockImplementation(() => { throw new Error('Test error'); }); @@ -90,11 +85,10 @@ describe('Bulk update rules route', () => { }); it('returns a 403 error object if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.updateRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'put', path: DETECTION_ENGINE_RULES_BULK_UPDATE, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts index 7bec95bf90da9..b7bef15b5f6b1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/bulk_update_rules/route.ts @@ -17,9 +17,6 @@ import { import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { getIdBulkError } from '../../../utils/utils'; import { transformValidateBulkError } from '../../../utils/validate'; import { @@ -27,8 +24,7 @@ import { buildSiemResponse, createBulkErrorObject, } from '../../../../routes/utils'; -import { updateRules } from '../../../logic/crud/update_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { validateRulesWithDuplicatedDefaultExceptionsList } from '../../../logic/exceptions/validate_rules_with_duplicated_default_exceptions_list'; @@ -37,11 +33,7 @@ import { RULE_MANAGEMENT_BULK_ACTION_SOCKET_TIMEOUT_MS } from '../../timeouts'; /** * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead */ -export const bulkUpdateRulesRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'], - logger: Logger -) => { +export const bulkUpdateRulesRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { router.versioned .put({ access: 'public', @@ -69,16 +61,8 @@ export const bulkUpdateRulesRoute = ( try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rules = await Promise.all( request.body.map(async (payloadRule) => { @@ -93,14 +77,16 @@ export const bulkUpdateRulesRoute = ( }); } - throwAuthzError(await mlAuthz.validateRuleType(payloadRule.type)); - const existingRule = await readRules({ rulesClient, ruleId: payloadRule.rule_id, id: payloadRule.id, }); + if (!existingRule) { + return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); + } + validateRulesWithDuplicatedDefaultExceptionsList({ allRules: request.body, exceptionsList: payloadRule.exceptions_list, @@ -113,16 +99,11 @@ export const bulkUpdateRulesRoute = ( ruleId: payloadRule.id, }); - const rule = await updateRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.updateRule({ ruleUpdate: payloadRule, }); - if (rule != null) { - return transformValidateBulkError(rule.id, rule); - } else { - return getIdBulkError({ id: payloadRule.id, ruleId: payloadRule.rule_id }); - } + + return transformValidateBulkError(rule.id, rule); } catch (err) { return transformBulkError(idOrRuleIdOrUnknown, err); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts index 5abdc9b4904e4..4b23757112bdd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts @@ -14,33 +14,31 @@ import { createMlRuleRequest, getBasicEmptySearchResponse, } from '../../../../routes/__mocks__/request_responses'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__'; import { createRuleRoute } from './route'; import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Create rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no current rules clients.rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams())); // creation succeeds + clients.rulesManagementClient.createCustomRule.mockResolvedValue( + getRuleMock(getQueryRuleParams()) + ); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - createRuleRoute(server.router, ml); + createRuleRoute(server.router); }); describe('status codes', () => { @@ -73,10 +71,8 @@ describe('Create rule route', () => { }); test('returns a 403 if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject( @@ -107,7 +103,7 @@ describe('Create rule route', () => { }); test('catches error if creation throws', async () => { - clients.rulesClient.create.mockImplementation(async () => { + clients.rulesManagementClient.createCustomRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts index e9c84c1c50f5c..8ab0af5cda742 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.ts @@ -13,22 +13,15 @@ import { validateCreateRuleProps, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { createRules } from '../../../logic/crud/create_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate'; -export const createRuleRoute = ( - router: SecuritySolutionPluginRouter, - ml: SetupPlugins['ml'] -): void => { +export const createRuleRoute = (router: SecuritySolutionPluginRouter): void => { router.versioned .post({ access: 'public', @@ -64,7 +57,7 @@ export const createRuleRoute = ( ]); const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const exceptionsClient = ctx.lists?.getExceptionListClient(); if (request.body.rule_id != null) { @@ -81,14 +74,6 @@ export const createRuleRoute = ( } } - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - throwAuthzError(await mlAuthz.validateRuleType(request.body.type)); - // This will create the endpoint list if it does not exist yet await exceptionsClient?.createEndpointList(); checkDefaultRuleExceptionListReferences({ @@ -104,8 +89,7 @@ export const createRuleRoute = ( await validateResponseActionsPermissions(ctx.securitySolution, request.body); - const createdRule = await createRules({ - rulesClient, + const createdRule = await rulesManagementClient.createCustomRule({ params: request.body, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts index 11dd8946419f9..74d6ed952cf15 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.test.ts @@ -27,6 +27,7 @@ describe('Delete rule route', () => { ({ clients, context } = requestContextMock.createTools()); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); + clients.rulesManagementClient.deleteRule.mockResolvedValue(); clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); deleteRuleRoute(server.router); @@ -68,7 +69,7 @@ describe('Delete rule route', () => { }); test('catches error if deletion throws error', async () => { - clients.rulesClient.delete.mockImplementation(async () => { + clients.rulesManagementClient.deleteRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts index f45901082d393..7db52619ff0b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/delete_rule/route.ts @@ -16,8 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { deleteRules } from '../../../logic/crud/delete_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdError, transform } from '../../../utils/utils'; export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { @@ -50,6 +49,7 @@ export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { const ctx = await context.resolve(['core', 'securitySolution', 'alerting']); const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const rule = await readRules({ rulesClient, id, ruleId }); @@ -61,9 +61,8 @@ export const deleteRuleRoute = (router: SecuritySolutionPluginRouter) => { }); } - await deleteRules({ + await rulesManagementClient.deleteRule({ ruleId: rule.id, - rulesClient, }); const transformed = transform(rule); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts index da4f9e67d7b8f..1320c8d5a3296 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.test.ts @@ -13,9 +13,6 @@ import { rulesToNdJsonString, } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; - import type { requestMock } from '../../../../routes/__mocks__'; import { createMockConfig, requestContextMock, serverMock } from '../../../../routes/__mocks__'; import { buildHapiStream } from '../../../../routes/__mocks__/utils'; @@ -31,6 +28,7 @@ import { import * as createRulesAndExceptionsStreamFromNdJson from '../../../logic/import/create_rules_stream_from_ndjson'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { importRulesRoute } from './route'; +import { HttpAuthzError } from '../../../../../machine_learning/validation'; jest.mock('../../../../../machine_learning/authz'); @@ -39,7 +37,6 @@ describe('Import rules route', () => { let server: ReturnType; let request: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); @@ -47,15 +44,15 @@ describe('Import rules route', () => { config = createMockConfig(); const hapiStream = buildHapiStream(ruleIdsToNdJsonString(['rule-1'])); request = getImportRulesRequest(hapiStream); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + clients.rulesManagementClient.importRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.actionsClient.getAll.mockResolvedValue([]); context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - importRulesRoute(server.router, config, ml); + importRulesRoute(server.router, config); }); describe('status codes', () => { @@ -83,13 +80,12 @@ describe('Import rules route', () => { describe('unhappy paths', () => { test('returns a 403 error object if ML Authz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.importRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(response.status).toEqual(200); expect(response.body).toEqual({ errors: [ @@ -189,6 +185,10 @@ describe('Import rules route', () => { describe('rule with existing rule_id', () => { test('returns with reported conflict if `overwrite` is set to `false`', async () => { clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // extant rule + clients.rulesManagementClient.importRule.mockRejectedValue({ + message: 'rule_id: "rule-1" already exists', + statusCode: 409, + }); const response = await server.inject(request, requestContextMock.convertContext(context)); expect(response.status).toEqual(200); @@ -403,6 +403,10 @@ describe('Import rules route', () => { }); test('returns with reported conflict if `overwrite` is set to `false`', async () => { + clients.rulesManagementClient.importRule.mockRejectedValueOnce({ + message: 'rule_id: "rule-1" already exists', + statusCode: 409, + }); const multiRequest = getImportRulesRequest( buildHapiStream(ruleIdsToNdJsonString(['rule-1', 'rule-2', 'rule-3'])) ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts index 0e1013f452d66..a085aced46ab4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/import_rules/route.ts @@ -17,10 +17,8 @@ import { } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; import type { ConfigType } from '../../../../../../config'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import type { BulkError, ImportRuleResponse } from '../../../../routes/utils'; import { buildSiemResponse, isBulkError, isImportRegular } from '../../../../routes/utils'; import { importRuleActionConnectors } from '../../../logic/import/action_connectors/import_rule_action_connectors'; @@ -37,11 +35,7 @@ import { RULE_MANAGEMENT_IMPORT_EXPORT_SOCKET_TIMEOUT_MS } from '../../timeouts' const CHUNK_PARSED_OBJECT_SIZE = 50; -export const importRulesRoute = ( - router: SecuritySolutionPluginRouter, - config: ConfigType, - ml: SetupPlugins['ml'] -) => { +export const importRulesRoute = (router: SecuritySolutionPluginRouter, config: ConfigType) => { router.versioned .post({ access: 'public', @@ -80,7 +74,7 @@ export const importRulesRoute = ( 'licensing', ]); - const rulesClient = ctx.alerting.getRulesClient(); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); const actionsClient = ctx.actions.getActionsClient(); const actionSOClient = ctx.core.savedObjects.getClient({ includedHiddenTypes: ['action'], @@ -90,13 +84,6 @@ export const importRulesRoute = ( const savedObjectsClient = ctx.core.savedObjects.client; const exceptionsClient = ctx.lists?.getExceptionListClient(); - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - const { filename } = (request.body.file as HapiReadableStream).hapi; const fileExtension = extname(filename).toLowerCase(); if (fileExtension !== '.ndjson') { @@ -166,9 +153,8 @@ export const importRulesRoute = ( const importRuleResponse: ImportRuleResponse[] = await importRulesHelper({ ruleChunks: chunkParseObjects, rulesResponseAcc: [...actionConnectorErrors, ...duplicateIdErrors], - mlAuthz, overwriteRules: request.query.overwrite, - rulesClient, + rulesManagementClient, existingLists: foundReferencedExceptionLists, allowMissingConnectorSecrets: !!actionConnectors.length, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts index 1255287cf52f5..22a2c9c058895 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.test.ts @@ -8,9 +8,6 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; import { getPatchRulesSchemaMock } from '../../../../../../../common/api/detection_engine/rule_management/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { mlServicesMock } from '../../../../../machine_learning/mocks'; - import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__'; import { getEmptyFindResult, @@ -24,24 +21,22 @@ import { import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks'; import { patchRuleRoute } from './route'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Patch rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update + clients.rulesManagementClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); - patchRuleRoute(server.router, ml); + patchRuleRoute(server.router); }); describe('status codes', () => { @@ -50,6 +45,7 @@ describe('Patch rule route', () => { getPatchRequest(), requestContextMock.convertContext(context) ); + expect(response.status).toEqual(200); }); @@ -80,7 +76,7 @@ describe('Patch rule route', () => { }); test('catches error if update throws error', async () => { - clients.rulesClient.update.mockImplementation(async () => { + clients.rulesManagementClient.patchRule.mockImplementation(async () => { throw new Error('Test error'); }); const response = await server.inject( @@ -100,36 +96,39 @@ describe('Patch rule route', () => { ...getFindResultWithSingleHit(), data: [getRuleMock(getMlRuleParams())], }); + + const anomalyThreshold = 4; + const machineLearningJobId = 'some_job_id'; + clients.rulesManagementClient.patchRule.mockResolvedValueOnce( + getRuleMock( + getMlRuleParams({ + anomalyThreshold, + machineLearningJobId: [machineLearningJobId], + }) + ) + ); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_URL, body: { type: 'machine_learning', rule_id: 'my-rule-id', - anomaly_threshold: 4, - machine_learning_job_id: 'some_job_id', + anomaly_threshold: anomalyThreshold, + machine_learning_job_id: machineLearningJobId, }, }); - await server.inject(request, requestContextMock.convertContext(context)); - - expect(clients.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 4, - machineLearningJobId: ['some_job_id'], - }), - }), - }) - ); + const response = await server.inject(request, requestContextMock.convertContext(context)); + expect(response.status).toEqual(200); + expect(response.body.machine_learning_job_id).toEqual([machineLearningJobId]); + expect(response.body.anomaly_threshold).toEqual(anomalyThreshold); }); it('rejects patching a rule to ML if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const request = requestMock.create({ method: 'patch', path: DETECTION_ENGINE_RULES_URL, @@ -145,11 +144,10 @@ describe('Patch rule route', () => { }); it('rejects patching an ML rule if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.patchRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); + const { type, ...payloadWithoutType } = typicalMlRulePayload(); const request = requestMock.create({ method: 'patch', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts index 5401e2361ca58..6cbba8ce65759 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/patch_rule/route.ts @@ -13,20 +13,16 @@ import { validatePatchRuleRequestBody, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { patchRules } from '../../../logic/crud/patch_rules'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { getIdError } from '../../../utils/utils'; import { transformValidate } from '../../../utils/validate'; -export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { +export const patchRuleRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .patch({ access: 'public', @@ -56,27 +52,20 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPl try { const params = request.body; const rulesClient = (await context.alerting).getRulesClient(); - const savedObjectsClient = (await context.core).savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: (await context.licensing).license, - ml, - request, - savedObjectsClient, - }); - if (params.type) { - // reject an unauthorized "promotion" to ML - throwAuthzError(await mlAuthz.validateRuleType(params.type)); - } + const rulesManagementClient = (await context.securitySolution).getRulesManagementClient(); const existingRule = await readRules({ rulesClient, ruleId: params.rule_id, id: params.id, }); - if (existingRule?.params.type) { - // reject an unauthorized modification of an ML rule - throwAuthzError(await mlAuthz.validateRuleType(existingRule?.params.type)); + + if (!existingRule) { + const error = getIdError({ id: params.id, ruleId: params.rule_id }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); } checkDefaultRuleExceptionListReferences({ exceptionLists: params.exceptions_list }); @@ -87,22 +76,13 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPl ruleId: params.id, }); - const rule = await patchRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.patchRule({ nextParams: params, }); - if (rule != null && rule.enabled != null && rule.name != null) { - return response.ok({ - body: transformValidate(rule), - }); - } else { - const error = getIdError({ id: params.id, ruleId: params.rule_id }); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); - } + + return response.ok({ + body: transformValidate(rule), + }); } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts index ade1f280c046d..aabf538367930 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/read_rule/route.ts @@ -16,7 +16,7 @@ import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constant import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { readRules } from '../../../logic/crud/read_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { getIdError, transform } from '../../../utils/utils'; export const readRuleRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts index 4eea0dc1797d5..624eaf882b0fa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts @@ -4,9 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { mlServicesMock } from '../../../../../machine_learning/mocks'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; import { getEmptyFindResult, getRuleMock, @@ -24,25 +21,23 @@ import { } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks'; import { getQueryRuleParams } from '../../../../rule_schema/mocks'; import { ResponseActionTypesEnum } from '../../../../../../../common/api/detection_engine'; - -jest.mock('../../../../../machine_learning/authz'); +import { HttpAuthzError } from '../../../../../machine_learning/validation'; describe('Update rule route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); - let ml: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - ml = mlServicesMock.createSetupContract(); clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update + clients.rulesManagementClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams())); clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - updateRuleRoute(server.router, ml); + updateRuleRoute(server.router); }); describe('status codes', () => { @@ -99,10 +94,8 @@ describe('Update rule route', () => { }); it('returns a 403 if mlAuthz fails', async () => { - (buildMlAuthz as jest.Mock).mockReturnValueOnce({ - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: false, message: 'mocked validation message' }), + clients.rulesManagementClient.updateRule.mockImplementationOnce(async () => { + throw new HttpAuthzError('mocked validation message'); }); const request = requestMock.create({ method: 'put', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts index 215a0827b015f..55bd63f1e0965 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.ts @@ -13,20 +13,16 @@ import { validateUpdateRuleProps, } from '../../../../../../../common/api/detection_engine/rule_management'; import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants'; -import type { SetupPlugins } from '../../../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../../../types'; import { buildRouteValidationWithZod } from '../../../../../../utils/build_validation/route_validation'; -import { buildMlAuthz } from '../../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../../machine_learning/validation'; import { buildSiemResponse } from '../../../../routes/utils'; -import { readRules } from '../../../logic/crud/read_rules'; -import { updateRules } from '../../../logic/crud/update_rules'; +import { readRules } from '../../../logic/rule_management/read_rules'; import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list'; import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list'; import { getIdError } from '../../../utils/utils'; import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate'; -export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml']) => { +export const updateRuleRoute = (router: SecuritySolutionPluginRouter) => { router.versioned .put({ access: 'public', @@ -52,17 +48,8 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupP } try { const ctx = await context.resolve(['core', 'securitySolution', 'alerting', 'licensing']); - const rulesClient = ctx.alerting.getRulesClient(); - const savedObjectsClient = ctx.core.savedObjects.client; - - const mlAuthz = buildMlAuthz({ - license: ctx.licensing.license, - ml, - request, - savedObjectsClient, - }); - throwAuthzError(await mlAuthz.validateRuleType(request.body.type)); + const rulesManagementClient = ctx.securitySolution.getRulesManagementClient(); checkDefaultRuleExceptionListReferences({ exceptionLists: request.body.exceptions_list }); @@ -79,29 +66,27 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter, ml: SetupP id: request.body.id, }); + if (existingRule == null) { + const error = getIdError({ id: request.body.id, ruleId: request.body.rule_id }); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + await validateResponseActionsPermissions( ctx.securitySolution, request.body, existingRule ); - const rule = await updateRules({ - rulesClient, - existingRule, + const rule = await rulesManagementClient.updateRule({ ruleUpdate: request.body, }); - if (rule != null) { - return response.ok({ - body: transformValidate(rule), - }); - } else { - const error = getIdError({ id: request.body.id, ruleId: request.body.rule_id }); - return siemResponse.error({ - body: error.message, - statusCode: error.statusCode, - }); - } + return response.ok({ + body: transformValidate(rule), + }); } catch (err) { const error = transformError(err); return siemResponse.error({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts deleted file mode 100644 index 59b7d9996b5b4..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.test.ts +++ /dev/null @@ -1,82 +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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { createRules } from './create_rules'; -import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; -import { - getCreateMachineLearningRulesSchemaMock, - getCreateThreatMatchRulesSchemaMock, -} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; - -describe('createRules', () => { - it('calls the rulesClient with legacy ML params', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ rulesClient, params: getCreateMachineLearningRulesSchemaMock() }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['typical-ml-job-id'], - }), - }), - }) - ); - }); - - it('calls the rulesClient with ML params', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ - rulesClient, - params: { - ...getCreateMachineLearningRulesSchemaMock(), - machine_learning_job_id: ['new_job_1', 'new_job_2'], - }, - }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - anomalyThreshold: 58, - machineLearningJobId: ['new_job_1', 'new_job_2'], - }), - }), - }) - ); - }); - - it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { - const rulesClient = rulesClientMock.create(); - const params = getCreateThreatMatchRulesSchemaMock(); - delete params.threat_indicator_path; - await createRules({ rulesClient, params }); - expect(rulesClient.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, - }), - }), - }) - ); - }); - - it('does not populate a threatIndicatorPath value for other rules if empty', async () => { - const rulesClient = rulesClientMock.create(); - await createRules({ rulesClient, params: getCreateMachineLearningRulesSchemaMock() }); - expect(rulesClient.create).not.toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, - }), - }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts deleted file mode 100644 index 0c39d0f39411f..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/create_rules.ts +++ /dev/null @@ -1,42 +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 type { SanitizedRule } from '@kbn/alerting-plugin/common'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; - -import type { RuleCreateProps } from '../../../../../../common/api/detection_engine/model/rule_schema'; -import { convertCreateAPIToInternalSchema } from '../../normalization/rule_converters'; -import type { RuleParams } from '../../../rule_schema'; - -export interface CreateRulesOptions { - rulesClient: RulesClient; - params: T; - id?: string; - immutable?: boolean; - defaultEnabled?: boolean; - allowMissingConnectorSecrets?: boolean; -} - -export const createRules = async ({ - rulesClient, - params, - id, - immutable = false, - defaultEnabled = true, - allowMissingConnectorSecrets, -}: CreateRulesOptions): Promise> => { - const internalRule = convertCreateAPIToInternalSchema(params, immutable, defaultEnabled); - const rule = await rulesClient.create({ - options: { - id, - }, - data: internalRule, - allowMissingConnectorSecrets, - }); - - return rule; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts deleted file mode 100644 index 23e09d8e35c62..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { RulesClient } from '@kbn/alerting-plugin/server'; -import type { RuleObjectId } from '../../../../../../common/api/detection_engine/model/rule_schema'; - -export interface DeleteRuleOptions { - ruleId: RuleObjectId; - rulesClient: RulesClient; -} - -export const deleteRules = async ({ ruleId, rulesClient }: DeleteRuleOptions) => { - await rulesClient.delete({ id: ruleId }); -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts deleted file mode 100644 index ede7b86bd737e..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { PartialRule, RulesClient } from '@kbn/alerting-plugin/server'; - -import type { PatchRuleRequestBody } from '../../../../../../common/api/detection_engine/rule_management'; -import type { RuleAlertType, RuleParams } from '../../../rule_schema'; -import { convertPatchAPIToInternalSchema } from '../../normalization/rule_converters'; - -export interface PatchRulesOptions { - rulesClient: RulesClient; - nextParams: PatchRuleRequestBody; - existingRule: RuleAlertType | null | undefined; - allowMissingConnectorSecrets?: boolean; - - shouldIncrementRevision?: boolean; -} - -export const patchRules = async ({ - rulesClient, - existingRule, - nextParams, - allowMissingConnectorSecrets, - shouldIncrementRevision = true, -}: PatchRulesOptions): Promise | null> => { - if (existingRule == null) { - return null; - } - - const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule); - - const update = await rulesClient.update({ - id: existingRule.id, - data: patchedRule, - allowMissingConnectorSecrets, - shouldIncrementRevision: () => shouldIncrementRevision, - }); - - if (existingRule.enabled && nextParams.enabled === false) { - await rulesClient.disable({ id: existingRule.id }); - } else if (!existingRule.enabled && nextParams.enabled === true) { - await rulesClient.enable({ id: existingRule.id }); - } else { - // enabled is null or undefined and we do not touch the rule - } - - if (nextParams.enabled != null) { - return { ...update, enabled: nextParams.enabled }; - } else { - return update; - } -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts deleted file mode 100644 index c30fdb107b83e..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.mock.ts +++ /dev/null @@ -1,26 +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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import { - getUpdateMachineLearningSchemaMock, - getUpdateRulesSchemaMock, -} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import { getRuleMock } from '../../../routes/__mocks__/request_responses'; -import { getQueryRuleParams } from '../../../rule_schema/mocks'; - -export const getUpdateRulesOptionsMock = () => ({ - rulesClient: rulesClientMock.create(), - existingRule: getRuleMock(getQueryRuleParams()), - ruleUpdate: getUpdateRulesSchemaMock(), -}); - -export const getUpdateMlRulesOptionsMock = () => ({ - rulesClient: rulesClientMock.create(), - existingRule: getRuleMock(getQueryRuleParams()), - ruleUpdate: getUpdateMachineLearningSchemaMock(), -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts deleted file mode 100644 index 6abba4dba0ab9..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.test.ts +++ /dev/null @@ -1,81 +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 type { RulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; -import { getRuleMock, resolveRuleMock } from '../../../routes/__mocks__/request_responses'; -import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; -import { updateRules } from './update_rules'; -import { getUpdateRulesOptionsMock, getUpdateMlRulesOptionsMock } from './update_rules.mock'; - -// Failing with rule registry enabled -describe('updateRules', () => { - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesOptionsMock = getUpdateRulesOptionsMock(); - rulesOptionsMock.ruleUpdate.enabled = false; - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getQueryRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.disable).toHaveBeenCalledWith( - expect.objectContaining({ - id: rulesOptionsMock.ruleUpdate.id, - }) - ); - }); - - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const baseRulesOptionsMock = getUpdateRulesOptionsMock(); - const rulesOptionsMock = { - ...baseRulesOptionsMock, - existingRule: { - ...baseRulesOptionsMock.existingRule, - enabled: false, - }, - }; - rulesOptionsMock.ruleUpdate.enabled = true; - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getQueryRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.enable).toHaveBeenCalledWith( - expect.objectContaining({ - id: rulesOptionsMock.ruleUpdate.id, - }) - ); - }); - - it('calls the rulesClient with params', async () => { - const rulesOptionsMock = getUpdateMlRulesOptionsMock(); - rulesOptionsMock.ruleUpdate.enabled = true; - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).update.mockResolvedValue( - getRuleMock(getMlRuleParams()) - ); - - (rulesOptionsMock.rulesClient as unknown as RulesClientMock).resolve.mockResolvedValue( - resolveRuleMock(getMlRuleParams()) - ); - - await updateRules(rulesOptionsMock); - - expect(rulesOptionsMock.rulesClient.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ - params: expect.objectContaining({ - type: 'machine_learning', - severity: 'high', - }), - }), - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts deleted file mode 100644 index ec790f9f6f71b..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/update_rules.ts +++ /dev/null @@ -1,97 +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. - */ - -/* eslint-disable complexity */ -import type { PartialRule, RulesClient } from '@kbn/alerting-plugin/server'; -import { DEFAULT_MAX_SIGNALS } from '../../../../../../common/constants'; -import type { RuleUpdateProps } from '../../../../../../common/api/detection_engine/model/rule_schema'; -import { transformRuleToAlertAction } from '../../../../../../common/detection_engine/transform_actions'; - -import type { InternalRuleUpdate, RuleParams, RuleAlertType } from '../../../rule_schema'; -import { transformToActionFrequency } from '../../normalization/rule_actions'; -import { typeSpecificSnakeToCamel } from '../../normalization/rule_converters'; -import { addEcsToRequiredFields } from '../../utils/utils'; - -export interface UpdateRulesOptions { - rulesClient: RulesClient; - existingRule: RuleAlertType | null | undefined; - ruleUpdate: RuleUpdateProps; - allowMissingConnectorSecrets?: boolean; -} - -export const updateRules = async ({ - rulesClient, - existingRule, - ruleUpdate, - allowMissingConnectorSecrets, -}: UpdateRulesOptions): Promise | null> => { - if (existingRule == null) { - return null; - } - - const alertActions = - ruleUpdate.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; - const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); - - const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); - const enabled = ruleUpdate.enabled ?? true; - - const newInternalRule: InternalRuleUpdate = { - name: ruleUpdate.name, - tags: ruleUpdate.tags ?? [], - params: { - author: ruleUpdate.author ?? [], - buildingBlockType: ruleUpdate.building_block_type, - description: ruleUpdate.description, - ruleId: existingRule.params.ruleId, - falsePositives: ruleUpdate.false_positives ?? [], - from: ruleUpdate.from ?? 'now-6m', - investigationFields: ruleUpdate.investigation_fields, - // Unlike the create route, immutable comes from the existing rule here - immutable: existingRule.params.immutable, - license: ruleUpdate.license, - outputIndex: ruleUpdate.output_index ?? '', - timelineId: ruleUpdate.timeline_id, - timelineTitle: ruleUpdate.timeline_title, - meta: ruleUpdate.meta, - maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, - relatedIntegrations: ruleUpdate.related_integrations ?? [], - requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), - riskScore: ruleUpdate.risk_score, - riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], - ruleNameOverride: ruleUpdate.rule_name_override, - setup: ruleUpdate.setup, - severity: ruleUpdate.severity, - severityMapping: ruleUpdate.severity_mapping ?? [], - threat: ruleUpdate.threat ?? [], - timestampOverride: ruleUpdate.timestamp_override, - timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, - to: ruleUpdate.to ?? 'now', - references: ruleUpdate.references ?? [], - namespace: ruleUpdate.namespace, - note: ruleUpdate.note, - version: ruleUpdate.version ?? existingRule.params.version, - exceptionsList: ruleUpdate.exceptions_list ?? [], - ...typeSpecificParams, - }, - schedule: { interval: ruleUpdate.interval ?? '5m' }, - actions, - }; - - const update = await rulesClient.update({ - id: existingRule.id, - data: newInternalRule, - allowMissingConnectorSecrets, - }); - - if (existingRule.enabled && enabled === false) { - await rulesClient.disable({ id: existingRule.id }); - } else if (!existingRule.enabled && enabled === true) { - await rulesClient.enable({ id: existingRule.id }); - } - return { ...update, enabled }; -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts index 7842febda6821..4ac14268a762b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.test.ts @@ -9,31 +9,19 @@ import { getImportRulesSchemaMock } from '../../../../../../common/api/detection import { getQueryRuleParams } from '../../../rule_schema/mocks'; import { requestContextMock } from '../../../routes/__mocks__'; -import { - getRuleMock, - getEmptyFindResult, - getFindResultWithSingleHit, - getFindResultWithMultiHits, -} from '../../../routes/__mocks__/request_responses'; +import { getRuleMock, getEmptyFindResult } from '../../../routes/__mocks__/request_responses'; -import { createRules } from '../crud/create_rules'; -import { updateRules } from '../crud/update_rules'; import { importRules } from './import_rules_utils'; - -jest.mock('../crud/create_rules'); -jest.mock('../crud/update_rules'); +import { createBulkErrorObject } from '../../../routes/utils'; describe('importRules', () => { - const mlAuthz = { - validateRuleType: jest - .fn() - .mockResolvedValue({ valid: true, message: 'mocked validation message' }), - }; const { clients, context } = requestContextMock.createTools(); + const importedRule = getRuleMock(getQueryRuleParams()); beforeEach(() => { clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); - clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + clients.rulesClient.update.mockResolvedValue(importedRule); + clients.rulesManagementClient.importRule.mockResolvedValue(importedRule); clients.actionsClient.getAll.mockResolvedValue([]); jest.clearAllMocks(); @@ -43,9 +31,8 @@ describe('importRules', () => { const result = await importRules({ ruleChunks: [], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); @@ -56,9 +43,8 @@ describe('importRules', () => { const result = await importRules({ ruleChunks: [[new Error('error importing')]], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); @@ -73,181 +59,43 @@ describe('importRules', () => { ]); }); - it('creates rule if no matching existing rule found', async () => { - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, + it('returns 409 error if ruleManagementClient throws with 409 - existing rule', async () => { + clients.rulesManagementClient.importRule.mockImplementationOnce(async () => { + throw createBulkErrorObject({ + ruleId: importedRule.params.ruleId, + statusCode: 409, + message: `rule_id: "${importedRule.params.ruleId}" already exists`, + }); }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); - }); - - it('reports error if "overwriteRules" is "false" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - + const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })]; const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], + ruleChunks: [ruleChunk], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([ - { - error: { message: 'rule_id: "rule-1" already exists', status_code: 409 }, - rule_id: 'rule-1', - }, - ]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); - }); - - it('updates rule if "overwriteRules" is "true" and matching rule found', async () => { - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - - const result = await importRules({ - ruleChunks: [ - [ - getImportRulesSchemaMock({ - rule_id: 'rule-1', - }), - ], - ], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).toHaveBeenCalled(); - }); - - /** - * Existing rule may have nullable fields set to a value (e.g. `timestamp_override` is set to `some.value`) but - * a rule to import doesn't have these fields set (e.g. `timestamp_override` is NOT present at all in the ndjson file). - * We expect the updated rule won't have such fields preserved (e.g. `timestamp_override` will be removed). - * - * Unit test is only able to check `updateRules()` receives a proper update object. - */ - it('ensures overwritten rule DOES NOT preserve fields missed in the imported rule when "overwriteRules" is "true" and matching rule found', async () => { - const existingRule = getRuleMock( - getQueryRuleParams({ - timestampOverride: 'some.value', - }) - ); - - clients.rulesClient.find.mockResolvedValue( - getFindResultWithMultiHits({ data: [existingRule] }) - ); - - const result = await importRules({ - ruleChunks: [ - [ - { - ...getImportRulesSchemaMock(), - rule_id: 'rule-1', - }, - ], - ], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([{ rule_id: 'rule-1', status_code: 200 }]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).toHaveBeenCalledWith( - expect.objectContaining({ - ruleUpdate: expect.not.objectContaining({ - timestamp_override: expect.anything(), - timestampOverride: expect.anything(), - }), - }) - ); - }); - - it('reports error if rulesClient throws', async () => { - clients.rulesClient.find.mockRejectedValue(new Error('error reading rule')); - - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); expect(result).toEqual([ { error: { - message: 'error reading rule', - status_code: 400, + message: `rule_id: "${importedRule.params.ruleId}" already exists`, + status_code: 409, }, - rule_id: 'rule-1', + rule_id: importedRule.params.ruleId, }, ]); - expect(createRules).not.toHaveBeenCalled(); - expect(updateRules).not.toHaveBeenCalled(); }); - - it('reports error if "createRules" throws', async () => { - (createRules as jest.Mock).mockRejectedValue(new Error('error creating rule')); - + it('creates rule if no matching existing rule found', async () => { + const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })]; const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], + ruleChunks: [ruleChunk], rulesResponseAcc: [], - mlAuthz, overwriteRules: false, - rulesClient: context.alerting.getRulesClient(), - existingLists: {}, - }); - - expect(result).toEqual([ - { - error: { - message: 'error creating rule', - status_code: 400, - }, - rule_id: 'rule-1', - }, - ]); - }); - - it('reports error if "updateRules" throws', async () => { - (updateRules as jest.Mock).mockRejectedValue(new Error('import rule error')); - clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); - - const result = await importRules({ - ruleChunks: [[getImportRulesSchemaMock({ rule_id: 'rule-1' })]], - rulesResponseAcc: [], - mlAuthz, - overwriteRules: true, - rulesClient: context.alerting.getRulesClient(), + rulesManagementClient: context.securitySolution.getRulesManagementClient(), existingLists: {}, }); - expect(result).toEqual([ - { - error: { - message: 'import rule error', - status_code: 400, - }, - rule_id: 'rule-1', - }, - ]); + expect(result).toEqual([{ rule_id: importedRule.params.ruleId, status_code: 200 }]); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts index 179c4069596e7..5c1ac63ec8a70 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/import_rules_utils.ts @@ -12,17 +12,11 @@ import type { ExceptionListSchema, } from '@kbn/securitysolution-io-ts-list-types'; -import type { RulesClient } from '@kbn/alerting-plugin/server'; - import type { RuleToImport } from '../../../../../../common/api/detection_engine/rule_management'; import type { ImportRuleResponse } from '../../../routes/utils'; import { createBulkErrorObject } from '../../../routes/utils'; -import { createRules } from '../crud/create_rules'; -import { readRules } from '../crud/read_rules'; -import { updateRules } from '../crud/update_rules'; -import type { MlAuthz } from '../../../../machine_learning/authz'; -import { throwAuthzError } from '../../../../machine_learning/validation'; import { checkRuleExceptionReferences } from './check_rule_exception_references'; +import type { IRulesManagementClient } from '../rule_management/rules_management_client'; export type PromiseFromStreams = RuleToImport | Error; export interface RuleExceptionsPromiseFromStreams { @@ -40,7 +34,7 @@ export interface RuleExceptionsPromiseFromStreams { * @param mlAuthz {object} * @param overwriteRules {boolean} - whether to overwrite existing rules * with imported rules if their rule_id matches - * @param rulesClient {object} + * @param rulesManagementClient {object} * @param existingLists {object} - all exception lists referenced by * rules that were found to exist * @returns {Promise} an array of error and success messages from import @@ -48,17 +42,15 @@ export interface RuleExceptionsPromiseFromStreams { export const importRules = async ({ ruleChunks, rulesResponseAcc, - mlAuthz, overwriteRules, - rulesClient, + rulesManagementClient, existingLists, allowMissingConnectorSecrets, }: { ruleChunks: PromiseFromStreams[][]; rulesResponseAcc: ImportRuleResponse[]; - mlAuthz: MlAuthz; overwriteRules: boolean; - rulesClient: RulesClient; + rulesManagementClient: IRulesManagementClient; existingLists: Record; allowMissingConnectorSecrets?: boolean; }) => { @@ -96,54 +88,28 @@ export const importRules = async ({ importRuleResponse = [...importRuleResponse, ...exceptionErrors]; - throwAuthzError(await mlAuthz.validateRuleType(parsedRule.type)); - const rule = await readRules({ - rulesClient, - ruleId: parsedRule.rule_id, - id: undefined, + const importedRule = await rulesManagementClient.importRule({ + ruleToImport: { + ...parsedRule, + exceptions_list: [...exceptions], + }, + overwriteRules, + options: { + allowMissingConnectorSecrets, + }, }); - if (rule == null) { - await createRules({ - rulesClient, - params: { - ...parsedRule, - exceptions_list: [...exceptions], - }, - allowMissingConnectorSecrets, - }); - resolve({ - rule_id: parsedRule.rule_id, - status_code: 200, - }); - } else if (rule != null && overwriteRules) { - await updateRules({ - rulesClient, - existingRule: rule, - ruleUpdate: { - ...parsedRule, - exceptions_list: [...exceptions], - }, - }); - resolve({ - rule_id: parsedRule.rule_id, - status_code: 200, - }); - } else if (rule != null) { - resolve( - createBulkErrorObject({ - ruleId: parsedRule.rule_id, - statusCode: 409, - message: `rule_id: "${parsedRule.rule_id}" already exists`, - }) - ); - } + resolve({ + rule_id: importedRule.params.ruleId, + status_code: 200, + }); } catch (err) { + const { error, statusCode, message } = err; resolve( createBulkErrorObject({ ruleId: parsedRule.rule_id, - statusCode: err.statusCode ?? 400, - message: err.message, + statusCode: statusCode ?? error?.status_code ?? 400, + message: message ?? error?.message ?? 'unknown error', }) ); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts new file mode 100644 index 0000000000000..7a35c8db1a001 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/read_rules.ts @@ -0,0 +1,21 @@ +/* + * 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 type { SanitizedRule } from '@kbn/alerting-plugin/common'; +import { getQueryRuleParams } from '../../../../rule_schema/mocks'; +import { getRuleMock } from '../../../../routes/__mocks__/request_responses'; +import type { RuleParams } from '../../../../rule_schema'; + +export const readRules = jest + .fn() + .mockImplementation(async (): Promise | null> => { + const mockRule: SanitizedRule = getRuleMock({ + ...getQueryRuleParams(), + ruleId: 'rule-id', + }); + return mockRule; + }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts new file mode 100644 index 0000000000000..629bd43f84c2e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/__mocks__/rules_management_client.ts @@ -0,0 +1,74 @@ +/* + * 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 type { + IRulesManagementClient, + CreateRuleOptions, + _UpdateRuleProps, + _PatchRuleProps, +} from '../rules_management_client'; +import type { RulesClient } from '@kbn/alerting-plugin/server'; +import type { + RuleCreateProps, + RuleObjectId, +} from '../../../../../../../common/api/detection_engine'; +import type { PrebuiltRuleAsset } from '../../../../prebuilt_rules'; +import type { RuleAlertType } from '../../../../rule_schema'; + +export type RulesManagementClientMock = jest.Mocked; + +const createRulesManagementClientMock = () => { + const mocked: RulesManagementClientMock = { + createCustomRule: jest.fn(), + createPrebuiltRule: jest.fn(), + updateRule: jest.fn(), + patchRule: jest.fn(), + deleteRule: jest.fn(), + upgradePrebuiltRule: jest.fn(), + importRule: jest.fn(), + }; + return mocked; +}; + +export const rulesManagementClientMock: { + create: () => RulesManagementClientMock; +} = { + create: createRulesManagementClientMock, +}; + +/* Mocks for internal methods */ +export const _createRule: jest.Mock< + ( + rulesClient: RulesClient, + params: RuleCreateProps, + options: CreateRuleOptions + ) => Promise +> = jest.fn(); + +export const _updateRule: jest.Mock< + (rulesClient: RulesClient, updateRulePayload: _UpdateRuleProps) => Promise +> = jest.fn(); + +export const patchRuleMock: jest.Mock< + (rulesClient: RulesClient, patchRulePayload: _PatchRuleProps) => Promise +> = jest.fn(); + +export const _upgradePrebuiltRuleWithTypeChange: jest.Mock< + ( + rulesClient: RulesClient, + ruleAsset: PrebuiltRuleAsset, + existingRule: RuleAlertType + ) => Promise +> = jest.fn(); + +export const _toggleRuleEnabledOnUpdate: jest.Mock< + (rulesClient: RulesClient, existingRule: RuleAlertType, enabled: boolean) => Promise +> = jest.fn(); + +export const _deleteRule: jest.Mock< + (rulesClient: RulesClient, deleteRulePayload: { ruleId: RuleObjectId }) => Promise +> = jest.fn(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts new file mode 100644 index 0000000000000..450c767f7b3ce --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_custom_rule.rule_management_client.test.ts @@ -0,0 +1,155 @@ +/* + * 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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { createCustomRule } from './rules_management_client'; + +import { + getCreateRulesSchemaMock, + getCreateMachineLearningRulesSchemaMock, + getCreateThreatMatchRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +describe('RuleManagementClient.createCustomRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + jest.resetAllMocks(); + rulesClient = rulesClientMock.create(); + }); + + it('should create a rule with the correct parameters and options', async () => { + const params = getCreateRulesSchemaMock(); + + await createCustomRule(rulesClient, { params }, mlAuthz); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: true, + params: expect.objectContaining({ + description: params.description, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect( + createCustomRule(rulesClient, { params: getCreateMachineLearningRulesSchemaMock() }, mlAuthz) + ).rejects.toThrow('mocked MLAuth error'); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + it('calls the rulesClient with legacy ML params', async () => { + await createCustomRule( + rulesClient, + { params: getCreateMachineLearningRulesSchemaMock() }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['typical-ml-job-id'], + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('calls the rulesClient with ML params', async () => { + await createCustomRule( + rulesClient, + { + params: { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + }, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { + const params = getCreateThreatMatchRulesSchemaMock(); + delete params.threat_indicator_path; + + await createCustomRule( + rulesClient, + { + params, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('does not populate a threatIndicatorPath value for other rules if empty', async () => { + await createCustomRule(rulesClient, { params: getCreateRulesSchemaMock() }, mlAuthz); + expect(rulesClient.create).not.toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: false, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts new file mode 100644 index 0000000000000..ca9ecc6a0814d --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/create_prebuilt_rule.rule_management_client.test.ts @@ -0,0 +1,172 @@ +/* + * 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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { createPrebuiltRule } from './rules_management_client'; + +import { + getCreateRulesSchemaMock, + getCreateMachineLearningRulesSchemaMock, + getCreateThreatMatchRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../../common/constants'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +describe('RuleManagementClient.createPrebuiltRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + jest.resetAllMocks(); + rulesClient = rulesClientMock.create(); + }); + + it('creates a rule with the correct parameters and options', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + + await createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + name: ruleAsset.name, + params: expect.objectContaining({ + ruleId: ruleAsset.rule_id, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect(createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + it('calls the rulesClient with legacy ML params', async () => { + const ruleAsset = { + ...getCreateMachineLearningRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + anomalyThreshold: ruleAsset.anomaly_threshold, + machineLearningJobId: [ruleAsset.machine_learning_job_id], + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('calls the rulesClient with ML params', async () => { + const ruleAsset = { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + version: 1, + rule_id: 'rule-id', + }; + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('populates a threatIndicatorPath value for threat_match rule if empty', async () => { + const ruleAsset = { ...getCreateThreatMatchRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + delete ruleAsset.threat_indicator_path; + + await createPrebuiltRule( + rulesClient, + { + ruleAsset, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + + it('does not populate a threatIndicatorPath value for other rules if empty', async () => { + const ruleAsset = { ...getCreateRulesSchemaMock(), version: 1, rule_id: 'rule-id' }; + await createPrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.create).not.toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + enabled: false, + params: expect.objectContaining({ + threatIndicatorPath: DEFAULT_INDICATOR_SOURCE_PATH, + immutable: true, + }), + }), + options: {}, + allowMissingConnectorSecrets: undefined, + }) + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts similarity index 52% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts index c34adec573f15..e675d5e01590d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/delete_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/delete_rule.rule_management_client.test.ts @@ -6,24 +6,21 @@ */ import { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; -import type { DeleteRuleOptions } from './delete_rules'; -import { deleteRules } from './delete_rules'; +import { deleteRule } from './rules_management_client'; -describe('deleteRules', () => { +describe('RuleManagementClient.deleteRule', () => { let rulesClient: ReturnType; beforeEach(() => { rulesClient = rulesClientMock.create(); }); - it('should delete the rule along with its actions, and statuses', async () => { - const options: DeleteRuleOptions = { - ruleId: 'ruleId', - rulesClient, - }; + it('should call rulesClient.delete passing the expected ruleId', async () => { + const ruleId = 'ruleId'; + await deleteRule(rulesClient, { + ruleId, + }); - await deleteRules(options); - - expect(rulesClient.delete).toHaveBeenCalledWith({ id: options.ruleId }); + expect(rulesClient.delete).toHaveBeenCalledWith({ id: ruleId }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts new file mode 100644 index 0000000000000..891c4f613b3c8 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/import_rule.rule_management_client.test.ts @@ -0,0 +1,185 @@ +/* + * 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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; +import { importRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { getCreateRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getQueryRuleParams } from '../../../rule_schema/mocks'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.importRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + const immutable = false as const; // Can only take value of false + const allowMissingConnectorSecrets = true; + const ruleToImport = { + ...getCreateRulesSchemaMock(), + tags: ['import-tag'], + rule_id: 'rule-id', + version: 1, + immutable, + }; + const existingRule = getRuleMock({ + ...getQueryRuleParams({ + ruleId: ruleToImport.rule_id, + }), + }); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls rulesClient.create with the correct parameters when rule_id does not match an installed rule', async () => { + (readRules as jest.Mock).mockResolvedValue(null); + await importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleToImport.name, + tags: ruleToImport.tags, + params: expect.objectContaining({ + immutable, + ruleId: ruleToImport.rule_id, + version: ruleToImport.version, + }), + }), + options: {}, + allowMissingConnectorSecrets, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + await expect( + importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ) + ).rejects.toThrow('mocked MLAuth error'); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.update).not.toHaveBeenCalled(); + }); + + describe('when rule_id matches an installed rule', () => { + it('calls rulesClient.update with the correct parameters when overwriteRules is true', async () => { + (readRules as jest.Mock).mockResolvedValue(existingRule); + await importRule( + rulesClient, + { + ruleToImport, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleToImport.name, + tags: ruleToImport.tags, + params: expect.objectContaining({ + index: ruleToImport.index, + description: ruleToImport.description, + }), + }), + id: existingRule.id, + }) + ); + }); + + /** + * Existing rule may have nullable fields set to a value (e.g. `timestamp_override` is set to `some.value`) but + * a rule to import doesn't have these fields set (e.g. `timestamp_override` is NOT present at all in the ndjson file). + * We expect the updated rule won't have such fields preserved (e.g. `timestamp_override` will be removed). + * + * Unit test is only able to check `updateRules()` receives a proper update object. + */ + it('ensures overwritten rule DOES NOT preserve fields missed in the imported rule when "overwriteRules" is "true" and matching rule found', async () => { + const existingRuleWithTimestampOverride = { + ...existingRule, + params: { + ...existingRule.params, + timestamp_override: '2020-01-01T00:00:00Z', + }, + }; + (readRules as jest.Mock).mockResolvedValue(existingRuleWithTimestampOverride); + + await importRule( + rulesClient, + { + ruleToImport: { + ...ruleToImport, + timestamp_override: undefined, + }, + overwriteRules: true, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + data: expect.not.objectContaining({ + timestamp_override: expect.anything(), + timestampOverride: expect.anything(), + }), + }) + ); + }); + + it('rejects when overwriteRules is false', async () => { + (readRules as jest.Mock).mockResolvedValue(existingRule); + await expect( + importRule( + rulesClient, + { + ruleToImport, + overwriteRules: false, + options: { allowMissingConnectorSecrets }, + }, + mlAuthz + ) + ).rejects.toMatchObject({ + error: { + status_code: 409, + message: `rule_id: "${ruleToImport.rule_id}" already exists`, + }, + rule_id: ruleToImport.rule_id, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/patch_rule.rule_management_client.test.ts similarity index 64% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/patch_rule.rule_management_client.test.ts index 3191d25a40e02..a27d83bed85ca 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/patch_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/patch_rule.rule_management_client.test.ts @@ -7,59 +7,69 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; -import { patchRules } from './patch_rules'; import { getRuleMock } from '../../../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; import { getCreateMachineLearningRulesSchemaMock, getCreateRulesSchemaMock, } from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { patchRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; -describe('patchRules', () => { - it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { - const rulesClient = rulesClientMock.create(); - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: false, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: true, - }; +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.patchRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls the rulesClient with expected params', async () => { + const nextParams = getCreateRulesSchemaMock(); + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); - expect(rulesClient.disable).toHaveBeenCalledWith( + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ - id: existingRule.id, + data: expect.objectContaining({ + name: nextParams.name, + params: expect.objectContaining({ + ruleId: nextParams.rule_id, + description: nextParams.description, + }), + }), }) ); }); - it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { - const rulesClient = rulesClientMock.create(); - const nextParams = { - ...getCreateRulesSchemaMock(), - enabled: true, - }; - const existingRule = { - ...getRuleMock(getQueryRuleParams()), - enabled: false, - }; + it('returns rule enabled: true if the nexParams have enabled: true', async () => { + const nextParams = { ...getCreateRulesSchemaMock(), enabled: true }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); - expect(rulesClient.enable).toHaveBeenCalledWith( - expect.objectContaining({ - id: existingRule.id, - }) - ); + + const rule = await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rule.enabled).toBe(true); }); it('calls the rulesClient with legacy ML params', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = getCreateMachineLearningRulesSchemaMock(); const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -73,14 +83,16 @@ describe('patchRules', () => { }); it('calls the rulesClient with new ML params', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = { ...getCreateMachineLearningRulesSchemaMock(), machine_learning_job_id: ['new_job_1', 'new_job_2'], }; const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -93,9 +105,67 @@ describe('patchRules', () => { ); }); + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: false, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: true, + }; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.disable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: false, + }; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + + expect(rulesClient.enable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const nextParams = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + + await expect(patchRule(rulesClient, { nextParams }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + describe('regression tests', () => { it("updates the rule's actions if provided", async () => { - const rulesClient = rulesClientMock.create(); const nextParams = { ...getCreateRulesSchemaMock(), actions: [ @@ -110,8 +180,11 @@ describe('patchRules', () => { ], }; const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ @@ -132,7 +205,6 @@ describe('patchRules', () => { }); it('does not update actions if none are specified', async () => { - const rulesClient = rulesClientMock.create(); const nextParams = getCreateRulesSchemaMock(); delete nextParams.actions; const existingRule = getRuleMock(getQueryRuleParams()); @@ -146,8 +218,11 @@ describe('patchRules', () => { group: 'default', }, ]; + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); - await patchRules({ rulesClient, nextParams, existingRule }); + + await patchRule(rulesClient, { nextParams }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.test.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.test.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.ts similarity index 100% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/crud/read_rules.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/read_rules.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts new file mode 100644 index 0000000000000..c88e6883f1430 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/rules_management_client.ts @@ -0,0 +1,430 @@ +/* + * 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 type { RulesClient } from '@kbn/alerting-plugin/server'; + +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import type { LicensingApiRequestHandlerContext } from '@kbn/licensing-plugin/server'; +import type { SharedServices } from '@kbn/ml-plugin/server/shared_services'; +import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; +import type { MlAuthz } from '../../../../machine_learning/authz'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import type { + RuleCreateProps, + RuleObjectId, + RuleToImport, + PatchRuleRequestBody, + RuleUpdateProps, +} from '../../../../../../common/api/detection_engine'; + +import type { PrebuiltRuleAsset } from '../../../prebuilt_rules'; + +import { readRules } from './read_rules'; + +import { + convertPatchAPIToInternalSchema, + convertUpdateAPIToInternalSchema, + convertCreateAPIToInternalSchema, +} from '../../normalization/rule_converters'; +import { transformAlertToRuleAction } from '../../../../../../common/detection_engine/transform_actions'; +import type { RuleAlertType, RuleParams } from '../../../rule_schema'; +import { createBulkErrorObject } from '../../../routes/utils'; +import { getIdError } from '../../utils/utils'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +class ClientError extends Error { + public readonly statusCode: number; + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + } +} + +export interface CreateRuleOptions { + /* Optionally pass an ID to use for the rule document. If not provided, an ID will be generated. */ + /* This is the ES document ID, NOT the rule_id */ + id?: string; + immutable?: boolean; + defaultEnabled?: boolean; + allowMissingConnectorSecrets?: boolean; +} + +export interface _UpdateRuleProps { + existingRule: RuleAlertType; + ruleUpdate: RuleUpdateProps; +} + +export interface _PatchRuleProps { + existingRule: RuleAlertType; + nextParams: PatchRuleRequestBody; +} + +interface CreateCustomRuleProps { + params: RuleCreateProps; +} + +interface CreatePrebuiltRuleProps { + ruleAsset: PrebuiltRuleAsset; +} + +interface UpdateRuleProps { + ruleUpdate: RuleUpdateProps; +} + +interface PatchRuleProps { + nextParams: PatchRuleRequestBody; +} + +interface DeleteRuleProps { + ruleId: RuleObjectId; +} + +interface UpgradePrebuiltRuleProps { + ruleAsset: PrebuiltRuleAsset; +} + +interface ImportRuleOptions { + allowMissingConnectorSecrets?: boolean; +} + +interface ImportRuleProps { + ruleToImport: RuleToImport; + overwriteRules?: boolean; + options: ImportRuleOptions; +} + +export interface IRulesManagementClient { + createCustomRule: (createCustomRulePayload: CreateCustomRuleProps) => Promise; + createPrebuiltRule: ( + createPrebuiltRulePayload: CreatePrebuiltRuleProps + ) => Promise; + updateRule: (updateRulePayload: UpdateRuleProps) => Promise; + patchRule: (patchRulePayload: PatchRuleProps) => Promise; + deleteRule: (deleteRulePayload: DeleteRuleProps) => Promise; + upgradePrebuiltRule: ( + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps + ) => Promise; + importRule: (importRulePayload: ImportRuleProps) => Promise; +} + +export const createRulesManagementClient = ( + rulesClient: RulesClient, + request: KibanaRequest, + savedObjectsClient: SavedObjectsClientContract, + licensing: LicensingApiRequestHandlerContext, + ml?: SharedServices +): IRulesManagementClient => { + const mlAuthz = buildMlAuthz({ + license: licensing.license, + ml, + request, + savedObjectsClient, + }); + + const client = { + createCustomRule: async ( + createCustomRulePayload: CreateCustomRuleProps + ): Promise => { + return createCustomRule(rulesClient, createCustomRulePayload, mlAuthz); + }, + + createPrebuiltRule: async ( + createPrebuiltRulePayload: CreatePrebuiltRuleProps + ): Promise => { + return createPrebuiltRule(rulesClient, createPrebuiltRulePayload, mlAuthz); + }, + + updateRule: async (updateRulePayload: UpdateRuleProps): Promise => { + return updateRule(rulesClient, updateRulePayload, mlAuthz); + }, + + patchRule: async (patchRulePayload: PatchRuleProps): Promise => { + return patchRule(rulesClient, patchRulePayload, mlAuthz); + }, + + deleteRule: async (deleteRulePayload: DeleteRuleProps): Promise => { + return deleteRule(rulesClient, deleteRulePayload); + }, + + upgradePrebuiltRule: async ( + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps + ): Promise => { + return upgradePrebuiltRule(rulesClient, upgradePrebuiltRulePayload, mlAuthz); + }, + + importRule: async (importRulePayload: ImportRuleProps): Promise => { + return importRule(rulesClient, importRulePayload, mlAuthz); + }, + }; + + return client; +}; + +export const createCustomRule = async ( + rulesClient: RulesClient, + createCustomRulePayload: CreateCustomRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { params } = createCustomRulePayload; + await _validateMlAuth(mlAuthz, params.type); + + const rule = await _createRule(rulesClient, params, { immutable: false }); + return rule; +}; + +export const createPrebuiltRule = async ( + rulesClient: RulesClient, + createPrebuiltRulePayload: CreatePrebuiltRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleAsset } = createPrebuiltRulePayload; + + await _validateMlAuth(mlAuthz, ruleAsset.type); + + const rule = await _createRule(rulesClient, ruleAsset, { + immutable: true, + defaultEnabled: false, + }); + + return rule; +}; + +export const updateRule = async ( + rulesClient: RulesClient, + updateRulePayload: UpdateRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleUpdate } = updateRulePayload; + const { rule_id: ruleId, id } = ruleUpdate; + + await _validateMlAuth(mlAuthz, ruleUpdate.type); + + const existingRule = await readRules({ + rulesClient, + ruleId, + id, + }); + + if (existingRule == null) { + const error = getIdError({ id, ruleId }); + throw new ClientError(error.message, error.statusCode); + } + + const update = await _updateRule(rulesClient, { ruleUpdate, existingRule }); + + await _toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleUpdate.enabled); + + return { ...update, enabled: ruleUpdate.enabled ?? existingRule.enabled }; +}; + +export const patchRule = async ( + rulesClient: RulesClient, + patchRulePayload: PatchRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { nextParams } = patchRulePayload; + const { rule_id: ruleId, id } = nextParams; + + const existingRule = await readRules({ + rulesClient, + ruleId, + id, + }); + + if (existingRule == null) { + const error = getIdError({ id, ruleId }); + throw new ClientError(error.message, error.statusCode); + } + + await _validateMlAuth(mlAuthz, nextParams.type ?? existingRule.params.type); + + const update = await _patchRule(rulesClient, { existingRule, nextParams }); + + await _toggleRuleEnabledOnUpdate(rulesClient, existingRule, nextParams.enabled); + + if (nextParams.enabled != null) { + return { ...update, enabled: nextParams.enabled }; + } else { + return update; + } +}; + +export const deleteRule = async ( + rulesClient: RulesClient, + deleteRulePayload: DeleteRuleProps +): Promise => { + const { ruleId } = deleteRulePayload; + await rulesClient.delete({ id: ruleId }); +}; + +export const upgradePrebuiltRule = async ( + rulesClient: RulesClient, + upgradePrebuiltRulePayload: UpgradePrebuiltRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleAsset } = upgradePrebuiltRulePayload; + + await _validateMlAuth(mlAuthz, ruleAsset.type); + + const existingRule = await readRules({ + rulesClient, + ruleId: ruleAsset.rule_id, + id: undefined, + }); + + if (!existingRule) { + throw new ClientError(`Failed to find rule ${ruleAsset.rule_id}`, 500); + } + + // If rule has change its type during upgrade, delete and recreate it + if (ruleAsset.type !== existingRule.params.type) { + return _upgradePrebuiltRuleWithTypeChange(rulesClient, ruleAsset, existingRule); + } + + // Else, simply patch it. + await _patchRule(rulesClient, { existingRule, nextParams: ruleAsset }); + + const updatedRule = await readRules({ + rulesClient, + ruleId: ruleAsset.rule_id, + id: undefined, + }); + + if (!updatedRule) { + throw new ClientError(`Rule ${ruleAsset.rule_id} not found after upgrade`, 500); + } + + return updatedRule; +}; + +export const importRule = async ( + rulesClient: RulesClient, + importRulePayload: ImportRuleProps, + mlAuthz: MlAuthz +): Promise => { + const { ruleToImport, overwriteRules, options } = importRulePayload; + + await _validateMlAuth(mlAuthz, ruleToImport.type); + + const existingRule = await readRules({ + rulesClient, + ruleId: ruleToImport.rule_id, + id: undefined, + }); + + if (!existingRule) { + return _createRule(rulesClient, ruleToImport, { + immutable: false, + allowMissingConnectorSecrets: options?.allowMissingConnectorSecrets, + }); + } else if (existingRule && overwriteRules) { + return _updateRule(rulesClient, { + existingRule, + ruleUpdate: ruleToImport, + }); + } else { + throw createBulkErrorObject({ + ruleId: existingRule.params.ruleId, + statusCode: 409, + message: `rule_id: "${existingRule.params.ruleId}" already exists`, + }); + } +}; + +/* -------- Internal Methods -------- */ +const _createRule = async ( + rulesClient: RulesClient, + params: RuleCreateProps, + options: CreateRuleOptions +) => { + const rulesClientCreateRuleOptions = options.id ? { id: options.id } : {}; + + const internalRule = convertCreateAPIToInternalSchema(params, options); + const rule = await rulesClient.create({ + data: internalRule, + options: rulesClientCreateRuleOptions, + allowMissingConnectorSecrets: options.allowMissingConnectorSecrets, + }); + + return rule; +}; + +const _updateRule = async ( + rulesClient: RulesClient, + updateRulePayload: _UpdateRuleProps +): Promise => { + const { ruleUpdate, existingRule } = updateRulePayload; + + const newInternalRule = convertUpdateAPIToInternalSchema({ + existingRule, + ruleUpdate, + }); + + const update = await rulesClient.update({ + id: existingRule.id, + data: newInternalRule, + }); + + return update; +}; + +const _patchRule = async ( + rulesClient: RulesClient, + patchRulePayload: _PatchRuleProps +): Promise => { + const { nextParams, existingRule } = patchRulePayload; + + const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule); + + const update = await rulesClient.update({ + id: existingRule.id, + data: patchedRule, + }); + + return update; +}; + +const _upgradePrebuiltRuleWithTypeChange = async ( + rulesClient: RulesClient, + ruleAsset: PrebuiltRuleAsset, + existingRule: RuleAlertType +) => { + // If we're trying to change the type of a prepackaged rule, we need to delete the old one + // and replace it with the new rule, keeping the enabled setting, actions, throttle, id, + // and exception lists from the old rule + await rulesClient.delete({ id: existingRule.id }); + + return _createRule( + rulesClient, + { + ...ruleAsset, + enabled: existingRule.enabled, + exceptions_list: existingRule.params.exceptionsList, + actions: existingRule.actions.map(transformAlertToRuleAction), + timeline_id: existingRule.params.timelineId, + timeline_title: existingRule.params.timelineTitle, + }, + { immutable: true, defaultEnabled: existingRule.enabled, id: existingRule.id } + ); +}; + +const _toggleRuleEnabledOnUpdate = async ( + rulesClient: RulesClient, + existingRule: RuleAlertType, + updatedRuleEnabled?: boolean +) => { + if (existingRule.enabled && updatedRuleEnabled === false) { + await rulesClient.disable({ id: existingRule.id }); + } else if (!existingRule.enabled && updatedRuleEnabled === true) { + await rulesClient.enable({ id: existingRule.id }); + } +}; + +const _validateMlAuth = async (mlAuthz: MlAuthz, ruleType: Type) => { + throwAuthzError(await mlAuthz.validateRuleType(ruleType)); +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts new file mode 100644 index 0000000000000..2d7bf7bc310e7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/update_rule.rule_management_client.test.ts @@ -0,0 +1,236 @@ +/* + * 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 { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; + +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getMlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; +import { + getCreateMachineLearningRulesSchemaMock, + getCreateRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import { updateRule } from './rules_management_client'; +import { readRules } from './read_rules'; +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); + +jest.mock('./read_rules'); + +describe('RuleManagementClient.updateRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('calls the rulesClient with expected params', async () => { + const ruleUpdate = getCreateRulesSchemaMock(); + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleUpdate.name, + params: expect.objectContaining({ + ruleId: ruleUpdate.rule_id, + description: ruleUpdate.description, + }), + }), + }) + ); + }); + + it('returns rule enabled: true if the nexParams have enabled: true', async () => { + const ruleUpdate = { ...getCreateRulesSchemaMock(), enabled: true }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + const rule = await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rule.enabled).toBe(true); + }); + + it('calls the rulesClient with legacy ML params', async () => { + const ruleUpdate = getCreateMachineLearningRulesSchemaMock(); + const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['typical-ml-job-id'], + }), + }), + }) + ); + }); + + it('calls the rulesClient with new ML params', async () => { + const ruleUpdate = { + ...getCreateMachineLearningRulesSchemaMock(), + machine_learning_job_id: ['new_job_1', 'new_job_2'], + }; + const existingRule = getRuleMock(getMlRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getMlRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + params: expect.objectContaining({ + anomalyThreshold: 58, + machineLearningJobId: ['new_job_1', 'new_job_2'], + }), + }), + }) + ); + }); + + it('should call rulesClient.disable if the rule was enabled and enabled is false', async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: false, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: true, + }; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.disable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('should call rulesClient.enable if the rule was disabled and enabled is true', async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + const existingRule = { + ...getRuleMock(getQueryRuleParams()), + enabled: false, + }; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.enable).toHaveBeenCalledWith( + expect.objectContaining({ + id: existingRule.id, + }) + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + enabled: true, + }; + + await expect(updateRule(rulesClient, { ruleUpdate }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + }); + + describe('regression tests', () => { + it("updates the rule's actions if provided", async () => { + const ruleUpdate = { + ...getCreateRulesSchemaMock(), + actions: [ + { + action_type_id: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ], + }; + const existingRule = getRuleMock(getQueryRuleParams()); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ], + }), + }) + ); + }); + + it('updates actions to empty if none are specified', async () => { + const ruleUpdate = getCreateRulesSchemaMock(); + delete ruleUpdate.actions; + const existingRule = getRuleMock(getQueryRuleParams()); + existingRule.actions = [ + { + actionTypeId: '.slack', + id: '2933e581-d81c-4fe3-88fe-c57c6b8a5bfd', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} signals', + }, + group: 'default', + }, + ]; + rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); + (readRules as jest.Mock).mockResolvedValueOnce(existingRule); + + await updateRule(rulesClient, { ruleUpdate }, mlAuthz); + + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + actions: [], + }), + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts new file mode 100644 index 0000000000000..eac57c1a6d368 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/rule_management/upgrade_prebuilt_rule.rule_management_client.test.ts @@ -0,0 +1,170 @@ +/* + * 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 { rulesClientMock } from '@kbn/alerting-plugin/server/mocks'; + +import { upgradePrebuiltRule } from './rules_management_client'; + +import { + getCreateEqlRuleSchemaMock, + getCreateRulesSchemaMock, +} from '../../../../../../common/api/detection_engine/model/rule_schema/mocks'; +import type { PrebuiltRuleAsset } from '../../../prebuilt_rules'; + +import { readRules } from './read_rules'; +import { getRuleMock } from '../../../routes/__mocks__/request_responses'; +import { getEqlRuleParams, getQueryRuleParams } from '../../../rule_schema/mocks'; + +import { buildMlAuthz } from '../../../../machine_learning/authz'; +import { throwAuthzError } from '../../../../machine_learning/validation'; + +jest.mock('../../../../machine_learning/authz'); +jest.mock('../../../../machine_learning/validation'); +jest.mock('./read_rules'); + +describe('RuleManagementClient.upgradePrebuiltRule', () => { + let rulesClient: ReturnType; + const mlAuthz = (buildMlAuthz as jest.Mock)(); + + beforeEach(() => { + rulesClient = rulesClientMock.create(); + }); + + it('throws if no matching rule_id is found', async () => { + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + + (readRules as jest.Mock).mockResolvedValue(null); + await expect(upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + `Failed to find rule ${ruleAsset.rule_id}` + ); + }); + + it('throws if mlAuth fails', async () => { + (throwAuthzError as jest.Mock).mockImplementationOnce(() => { + throw new Error('mocked MLAuth error'); + }); + + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateRulesSchemaMock(), + version: 1, + rule_id: 'rule-id', + }; + + await expect(upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz)).rejects.toThrow( + 'mocked MLAuth error' + ); + + expect(rulesClient.create).not.toHaveBeenCalled(); + expect(rulesClient.delete).not.toHaveBeenCalled(); + expect(rulesClient.update).not.toHaveBeenCalled(); + }); + + describe('if the new version has a different type than the existing version', () => { + // New version is "eql" + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateEqlRuleSchemaMock(), + tags: ['test'], + type: 'eql', + version: 1, + rule_id: 'rule-id', + }; + // Installed version is "query" + const installedRule = getRuleMock({ + ...getQueryRuleParams({ + exceptionsList: [ + { id: 'test_id', list_id: 'hi', type: 'detection', namespace_type: 'agnostic' }, + ], + }), + actions: [ + { + group: 'default', + id: 'test_id', + action_type_id: '.index', + config: { + index: ['index-1', 'index-2'], + }, + }, + ], + ruleId: 'rule-id', + }); + beforeEach(() => { + (readRules as jest.Mock).mockResolvedValue(installedRule); + }); + + it('deletes the old rule ', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.delete).toHaveBeenCalled(); + }); + + it('creates a new rule with the new type and expected params of the original rules', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleAsset.name, + tags: ruleAsset.tags, + // enabled and actions are kept from original rule + actions: installedRule.actions, + enabled: installedRule.enabled, + params: expect.objectContaining({ + index: ruleAsset.index, + description: ruleAsset.description, + immutable: true, + // exceptions_lists, actions, timeline_id and timeline_title are maintained + timelineTitle: installedRule.params.timelineTitle, + timelineId: installedRule.params.timelineId, + exceptionsList: installedRule.params.exceptionsList, + }), + }), + options: { + id: installedRule.id, // id is maintained + }, + allowMissingConnectorSecrets: undefined, + }) + ); + }); + }); + + describe('if the new version has the same type than the existing version', () => { + // New version is "eql" + const ruleAsset: PrebuiltRuleAsset = { + ...getCreateEqlRuleSchemaMock(), + tags: ['test'], + type: 'eql', + version: 1, + rule_id: 'rule-id', + }; + // Installed version is "eql" + const installedRule = getRuleMock({ + ...getEqlRuleParams(), + }); + beforeEach(() => { + (readRules as jest.Mock).mockResolvedValue(installedRule); + }); + + it('patches the existing rule with the new params from the rule asset', async () => { + await upgradePrebuiltRule(rulesClient, { ruleAsset }, mlAuthz); + expect(rulesClient.update).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ + name: ruleAsset.name, + tags: ruleAsset.tags, + params: expect.objectContaining({ + index: ruleAsset.index, + description: ruleAsset.description, + }), + }), + id: installedRule.id, + }) + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts index fd77213b178b5..e894a46642f9b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/normalization/rule_converters.ts @@ -21,8 +21,8 @@ import { import type { PatchRuleRequestBody } from '../../../../../common/api/detection_engine/rule_management'; import type { - RelatedIntegrationArray, RuleCreateProps, + RuleUpdateProps, TypeSpecificCreateProps, TypeSpecificResponse, } from '../../../../../common/api/detection_engine/model/rule_schema'; @@ -430,11 +430,68 @@ export const patchTypeSpecificSnakeToCamel = ( } }; +interface ConvertUpdateAPIToInternalSchemaProps { + existingRule: SanitizedRule; + ruleUpdate: RuleUpdateProps; +} + +export const convertUpdateAPIToInternalSchema = ({ + existingRule, + ruleUpdate, +}: ConvertUpdateAPIToInternalSchemaProps) => { + const alertActions = + ruleUpdate.actions?.map((action) => transformRuleToAlertAction(action)) ?? []; + const actions = transformToActionFrequency(alertActions, ruleUpdate.throttle); + + const typeSpecificParams = typeSpecificSnakeToCamel(ruleUpdate); + + const newInternalRule: InternalRuleUpdate = { + name: ruleUpdate.name, + tags: ruleUpdate.tags ?? [], + params: { + author: ruleUpdate.author ?? [], + buildingBlockType: ruleUpdate.building_block_type, + description: ruleUpdate.description, + ruleId: existingRule.params.ruleId, + falsePositives: ruleUpdate.false_positives ?? [], + from: ruleUpdate.from ?? 'now-6m', + investigationFields: ruleUpdate.investigation_fields, + immutable: existingRule.params.immutable, + license: ruleUpdate.license, + outputIndex: ruleUpdate.output_index ?? '', + timelineId: ruleUpdate.timeline_id, + timelineTitle: ruleUpdate.timeline_title, + meta: ruleUpdate.meta, + maxSignals: ruleUpdate.max_signals ?? DEFAULT_MAX_SIGNALS, + relatedIntegrations: ruleUpdate.related_integrations ?? [], + requiredFields: addEcsToRequiredFields(ruleUpdate.required_fields), + riskScore: ruleUpdate.risk_score, + riskScoreMapping: ruleUpdate.risk_score_mapping ?? [], + ruleNameOverride: ruleUpdate.rule_name_override, + setup: ruleUpdate.setup, + severity: ruleUpdate.severity, + severityMapping: ruleUpdate.severity_mapping ?? [], + threat: ruleUpdate.threat ?? [], + timestampOverride: ruleUpdate.timestamp_override, + timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, + to: ruleUpdate.to ?? 'now', + references: ruleUpdate.references ?? [], + namespace: ruleUpdate.namespace, + note: ruleUpdate.note, + version: ruleUpdate.version ?? existingRule.params.version, + exceptionsList: ruleUpdate.exceptions_list ?? [], + ...typeSpecificParams, + }, + schedule: { interval: ruleUpdate.interval ?? '5m' }, + actions, + }; + + return newInternalRule; +}; + // eslint-disable-next-line complexity export const convertPatchAPIToInternalSchema = ( - nextParams: PatchRuleRequestBody & { - related_integrations?: RelatedIntegrationArray; - }, + nextParams: PatchRuleRequestBody, existingRule: SanitizedRule ): InternalRuleUpdate => { const typeSpecificParams = patchTypeSpecificSnakeToCamel(nextParams, existingRule.params); @@ -489,13 +546,18 @@ export const convertPatchAPIToInternalSchema = ( }; }; +interface RuleCreateOptions { + immutable?: boolean; + defaultEnabled?: boolean; +} + +// eslint-disable-next-line complexity export const convertCreateAPIToInternalSchema = ( - input: RuleCreateProps & { - related_integrations?: RelatedIntegrationArray; - }, - immutable = false, - defaultEnabled = true + input: RuleCreateProps, + options?: RuleCreateOptions ): InternalRuleCreate => { + const { immutable = false, defaultEnabled = true } = options ?? {}; + const typeSpecificParams = typeSpecificSnakeToCamel(input); const newRuleId = input.rule_id ?? uuidv4(); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts index a2082b548660e..9975669d7e4a4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_monitoring/logic/detection_engine_health/rule_objects/fetch_rule_by_id.ts @@ -11,7 +11,7 @@ import type { RuleObjectId, RuleResponse, } from '../../../../../../../common/api/detection_engine/model/rule_schema'; -import { readRules } from '../../../../rule_management/logic/crud/read_rules'; +import { readRules } from '../../../../rule_management/logic/rule_management/read_rules'; import { transform } from '../../../../rule_management/utils/utils'; // TODO: https://github.com/elastic/kibana/issues/125642 Move to rule_management into a RuleManagementClient diff --git a/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts b/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts new file mode 100644 index 0000000000000..3112d258ef28c --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/machine_learning/__mocks__/validation.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const throwAuthzError = jest.fn(); +export const toAuthzError = jest.fn(); diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index ddb825cb642e6..28096984e0584 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -28,6 +28,7 @@ import type { EndpointAppContextService } from './endpoint/endpoint_app_context_ import { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import { createRulesManagementClient } from './lib/detection_engine/rule_management/logic/rule_management/rules_management_client'; export interface IRequestContextFactory { create( @@ -66,6 +67,7 @@ export class RequestContextFactory implements IRequestContextFactory { const [, startPlugins] = await core.getStartServices(); const frameworkRequest = await buildFrameworkRequest(context, security, request); const coreContext = await context.core; + const licensing = await context.licensing; const getSpaceId = (): string => startPlugins.spaces?.spacesService?.getSpaceId(request) || DEFAULT_SPACE_ID; @@ -111,6 +113,15 @@ export class RequestContextFactory implements IRequestContextFactory { getAuditLogger, + getRulesManagementClient: () => + createRulesManagementClient( + startPlugins.alerting.getRulesClientWithRequest(request), + request, + coreContext.savedObjects.client, + licensing, + plugins.ml + ), + getDetectionEngineHealthClient: memoize(() => ruleMonitoringService.createDetectionEngineHealthClient({ rulesClient: startPlugins.alerting.getRulesClientWithRequest(request), diff --git a/x-pack/plugins/security_solution/server/types.ts b/x-pack/plugins/security_solution/server/types.ts index 513eb6079e041..d8536c00ca4d0 100644 --- a/x-pack/plugins/security_solution/server/types.ts +++ b/x-pack/plugins/security_solution/server/types.ts @@ -33,6 +33,7 @@ import type { EndpointInternalFleetServicesInterface } from './endpoint/services import type { RiskEngineDataClient } from './lib/entity_analytics/risk_engine/risk_engine_data_client'; import type { RiskScoreDataClient } from './lib/entity_analytics/risk_score/risk_score_data_client'; import type { AssetCriticalityDataClient } from './lib/entity_analytics/asset_criticality'; +import type { IRulesManagementClient } from './lib/detection_engine/rule_management/logic/rule_management/rules_management_client'; export { AppClient }; export interface SecuritySolutionApiRequestHandlerContext { @@ -44,6 +45,7 @@ export interface SecuritySolutionApiRequestHandlerContext { getAppClient: () => AppClient; getSpaceId: () => string; getRuleDataService: () => IRuleDataService; + getRulesManagementClient: () => IRulesManagementClient; getDetectionEngineHealthClient: () => IDetectionEngineHealthClient; getRuleExecutionLog: () => IRuleExecutionLogForRoutes; getRacClient: (req: KibanaRequest) => Promise; From 5a2f1ac5baf9f2c815d02c11119e47800d80e4ee Mon Sep 17 00:00:00 2001 From: Kevin Delemme Date: Tue, 28 May 2024 10:03:26 -0400 Subject: [PATCH 37/59] feat(slo): prevent initial backfill (#184312) --- .../packages/kbn-slo-schema/src/schema/slo.ts | 1 + .../slo/docs/openapi/slo/bundled.json | 16 ++++++ .../slo/docs/openapi/slo/bundled.yaml | 12 +++++ .../slo/components/schemas/settings.yaml | 7 +++ .../slo/public/data/slo/slo.ts | 1 + .../slo/public/locators/slo_edit.test.ts | 2 +- .../slo_edit_form_objective_section.tsx | 41 ++++++++++++++- .../slo/public/pages/slo_edit/constants.ts | 9 ++++ .../process_slo_form_values.test.ts.snap | 30 +++++++++++ .../helpers/process_slo_form_values.ts | 13 +++++ .../slo/public/pages/slo_edit/types.ts | 3 ++ .../public/utils/slo/remote_slo_urls.test.ts | 4 +- .../domain/services/validate_slo.test.ts | 2 + .../__snapshots__/reset_slo.test.ts.snap | 2 + .../slo_definition_client.test.ts.snap | 1 + .../slo/server/services/create_slo.test.ts | 35 +++++++++++++ .../slo/server/services/create_slo.ts | 1 + .../slo/server/services/find_slo.test.ts | 1 + .../slo/server/services/fixtures/slo.ts | 1 + .../slo/server/services/get_slo.test.ts | 1 + .../slo/server/services/slo_repository.ts | 5 ++ .../apm_transaction_duration.test.ts | 24 +++++++++ .../apm_transaction_duration.ts | 12 +---- .../apm_transaction_error_rate.test.ts | 26 +++++++++- .../apm_transaction_error_rate.ts | 13 +---- .../transform_generators/common.test.ts | 50 ++++++++++++++++++- .../services/transform_generators/common.ts | 27 +++++++++- .../transform_generators/histogram.test.ts | 24 +++++++++ .../transform_generators/histogram.ts | 10 +--- .../transform_generators/kql_custom.test.ts | 24 +++++++++ .../transform_generators/kql_custom.ts | 10 +--- .../metric_custom.test.ts | 25 ++++++++++ .../transform_generators/metric_custom.ts | 10 +--- .../synthetics_availability.test.ts | 25 ++++++++++ .../synthetics_availability.ts | 10 ++-- .../timeslice_metric.test.ts | 25 ++++++++++ .../transform_generators/timeslice_metric.ts | 10 +--- .../remote_summary_doc_to_slo.test.ts.snap | 2 + .../remote_summary_doc_to_slo.ts | 7 ++- .../slo/server/services/update_slo.test.ts | 6 ++- .../slo/server/services/update_slo.ts | 11 ++++ .../api_integration/apis/slos/create_slo.ts | 1 + .../api_integration/apis/slos/update_slo.ts | 1 + 43 files changed, 472 insertions(+), 69 deletions(-) diff --git a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts index 692ec16050353..dcf18d0e3a82e 100644 --- a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts +++ b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts @@ -29,6 +29,7 @@ const objectiveSchema = t.intersection([ const settingsSchema = t.type({ syncDelay: durationType, frequency: durationType, + preventInitialBackfill: t.boolean, }); const groupBySchema = allOrAnyStringOrArray; diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json index 11bf01c8a7328..90f3e26fa4ed8 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.json @@ -190,6 +190,14 @@ "default": "asc" }, "example": "asc" + }, + { + "name": "hideStale", + "in": "query", + "description": "Hide stale SLOs from the list as defined by stale SLO threshold in SLO settings", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -1768,12 +1776,20 @@ "syncDelay": { "description": "The synch delay to apply to the transform. Default 1m", "type": "string", + "default": "1m", "example": "5m" }, "frequency": { "description": "Configure how often the transform runs, default 1m", "type": "string", + "default": "1m", "example": "5m" + }, + "preventInitialBackfill": { + "description": "Prevents the transform from backfilling data when it starts.", + "type": "boolean", + "default": false, + "example": true } } }, diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml index d099474448eae..6f826fb8f2e10 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/bundled.yaml @@ -121,6 +121,11 @@ paths: - desc default: asc example: asc + - name: hideStale + in: query + description: Hide stale SLOs from the list as defined by stale SLO threshold in SLO settings + schema: + type: boolean responses: '200': description: Successful request @@ -1210,11 +1215,18 @@ components: syncDelay: description: The synch delay to apply to the transform. Default 1m type: string + default: 1m example: 5m frequency: description: Configure how often the transform runs, default 1m type: string + default: 1m example: 5m + preventInitialBackfill: + description: Prevents the transform from backfilling data when it starts. + type: boolean + default: false + example: true summary_status: title: summary status type: string diff --git a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml index 2f4ff275f0d0e..a50ce0c28c136 100644 --- a/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml +++ b/x-pack/plugins/observability_solution/slo/docs/openapi/slo/components/schemas/settings.yaml @@ -5,8 +5,15 @@ properties: syncDelay: description: The synch delay to apply to the transform. Default 1m type: string + default: 1m example: 5m frequency: description: Configure how often the transform runs, default 1m type: string + default: 1m example: 5m + preventInitialBackfill: + description: Prevents the transform from backfilling data when it starts. + type: boolean + default: false + example: true diff --git a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts index 31ec29c44a31d..02541ac8a17c7 100644 --- a/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts +++ b/x-pack/plugins/observability_solution/slo/public/data/slo/slo.ts @@ -51,6 +51,7 @@ const baseSlo: Omit = { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts index 5ed997bcec129..ea85e58479636 100644 --- a/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts +++ b/x-pack/plugins/observability_solution/slo/public/locators/slo_edit.test.ts @@ -20,7 +20,7 @@ describe('SloEditLocator', () => { it('should return correct url when slo is provided', async () => { const location = await locator.getLocation(buildSlo({ id: 'foo' })); expect(location.path).toEqual( - "/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)" + "/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',groupings:(),id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',meta:(),name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',preventInitialBackfill:!f,syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)" ); }); }); diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx index ebb8250493748..15c51b1b86ce4 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/components/slo_edit_form_objective_section.tsx @@ -7,6 +7,7 @@ import { EuiCallOut, + EuiCheckbox, EuiFieldNumber, EuiFlexGrid, EuiFlexItem, @@ -18,10 +19,10 @@ import { useGeneratedHtmlId, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { TimeWindowType } from '@kbn/slo-schema'; import React, { useEffect, useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { FormattedMessage } from '@kbn/i18n-react'; import { BUDGETING_METHOD_OPTIONS, CALENDARALIGNED_TIMEWINDOW_OPTIONS, @@ -43,6 +44,7 @@ export function SloEditFormObjectiveSection() { const budgetingSelect = useGeneratedHtmlId({ prefix: 'budgetingSelect' }); const timeWindowTypeSelect = useGeneratedHtmlId({ prefix: 'timeWindowTypeSelect' }); const timeWindowSelect = useGeneratedHtmlId({ prefix: 'timeWindowSelect' }); + const preventBackfillCheckbox = useGeneratedHtmlId({ prefix: 'preventBackfill' }); const timeWindowType = watch('timeWindow.type'); const indicator = watch('indicator.type'); @@ -283,6 +285,43 @@ export function SloEditFormObjectiveSection() { + + + + + + + ( + + {i18n.translate('xpack.slo.sloEdit.settings.preventInitialBackfill.label', { + defaultMessage: 'Prevent initial backfill of data', + })} + + + } + checked={Boolean(field.value)} + onChange={(event: any) => onChange(event.target.checked)} + /> + )} + /> + + + ); } diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts index 2c79f9736fce7..123ebdc660947 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/constants.ts @@ -219,6 +219,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES: CreateSLOForm = { target: 99, }, groupBy: ALL_VALUE, + settings: { + preventInitialBackfill: false, + }, }; export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = { @@ -235,6 +238,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_CUSTOM_METRIC: CreateSLOForm = { target: 99, }, groupBy: ALL_VALUE, + settings: { + preventInitialBackfill: false, + }, }; export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm = { @@ -251,6 +257,9 @@ export const SLO_EDIT_FORM_DEFAULT_VALUES_SYNTHETICS_AVAILABILITY: CreateSLOForm target: 99, }, groupBy: SYNTHETICS_DEFAULT_GROUPINGS, + settings: { + preventInitialBackfill: false, + }, }; export const COMPARATOR_GT = i18n.translate('xpack.slo.sloEdit.sliType.timesliceMetric.gtLabel', { diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap index 4ff4e452a3475..3f7ac0ce83beb 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/__snapshots__/process_slo_form_values.test.ts.snap @@ -25,6 +25,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -70,6 +73,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -97,6 +103,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -136,6 +145,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -165,6 +177,9 @@ Object { "timesliceTarget": 95, "timesliceWindow": "2", }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -192,6 +207,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "1M", @@ -220,6 +238,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -249,6 +270,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -276,6 +300,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", @@ -303,6 +330,9 @@ Object { "objective": Object { "target": 99, }, + "settings": Object { + "preventInitialBackfill": false, + }, "tags": Array [], "timeWindow": Object { "duration": "30d", diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts index fd8bb96e2d715..8bbbcf9d2fee9 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/helpers/process_slo_form_values.ts @@ -50,6 +50,9 @@ export function transformSloResponseToCreateSloForm( }, groupBy: [values.groupBy].flat(), tags: values.tags, + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -76,6 +79,9 @@ export function transformCreateSLOFormToCreateSLOInput(values: CreateSLOForm): C }, tags: values.tags, groupBy: [values.groupBy].flat(), + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -102,6 +108,9 @@ export function transformValuesToUpdateSLOInput(values: CreateSLOForm): UpdateSL }, tags: values.tags, groupBy: [values.groupBy].flat(), + settings: { + preventInitialBackfill: values.settings?.preventInitialBackfill ?? false, + }, }; } @@ -211,5 +220,9 @@ export function transformPartialUrlStateToFormState( state.timeWindow = { duration: values.timeWindow.duration, type: values.timeWindow.type }; } + if (!!values.settings?.preventInitialBackfill) { + state.settings = { preventInitialBackfill: values.settings.preventInitialBackfill }; + } + return state; } diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts index d876e3a276208..5eef9a2d0e5ba 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts +++ b/x-pack/plugins/observability_solution/slo/public/pages/slo_edit/types.ts @@ -23,4 +23,7 @@ export interface CreateSLOForm { timesliceWindow?: string; }; groupBy: string[] | string; + settings: { + preventInitialBackfill: boolean; + }; } diff --git a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts index 28ca2bd977572..2c8476b9c2e09 100644 --- a/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts +++ b/x-pack/plugins/observability_solution/slo/public/utils/slo/remote_slo_urls.test.ts @@ -51,7 +51,7 @@ describe('remote SLO URLs Utils', () => { `"https://cloud.elast.co/app/slos/edit/fixed-id"` ); expect(createRemoteSloCloneUrl(remoteSlo)).toMatchInlineSnapshot( - `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` + `"https://cloud.elast.co/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` ); }); @@ -71,7 +71,7 @@ describe('remote SLO URLs Utils', () => { `"https://cloud.elast.co/s/my-custom-space/app/slos/edit/fixed-id"` ); expect(createRemoteSloCloneUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot( - `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` + `"https://cloud.elast.co/s/my-custom-space/app/slos/create?_a=(budgetingMethod:occurrences,createdAt:%272022-12-29T10:11:12.000Z%27,description:%27some%20description%20useful%27,enabled:!t,groupBy:%27*%27,groupings:(),indicator:(params:(filter:%27baz:%20foo%20and%20bar%20%3E%202%27,good:%27http_status:%202xx%27,index:some-index,timestampField:custom_timestamp,total:%27a%20query%27),type:sli.kql.custom),instanceId:%27*%27,meta:(),name:%27[Copy]%20super%20important%20level%20service%27,objective:(target:0.98),remote:(kibanaUrl:%27https:/cloud.elast.co/kibana%27,remoteName:remote_cluster),revision:1,settings:(frequency:%271m%27,preventInitialBackfill:!f,syncDelay:%271m%27),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:%2730d%27,type:rolling),updatedAt:%272022-12-29T10:11:12.000Z%27,version:2)"` ); }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts index 7a4a6fc40005b..5c06c89ee42aa 100644 --- a/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/domain/services/validate_slo.test.ts @@ -103,6 +103,7 @@ describe('validateSLO', () => { settings: { frequency: sixHours(), syncDelay: oneMinute(), + preventInitialBackfill: false, }, }); expect(() => validateSLO(slo)).toThrowError('Invalid settings.frequency'); @@ -113,6 +114,7 @@ describe('validateSLO', () => { settings: { frequency: oneMinute(), syncDelay: sixHours(), + preventInitialBackfill: false, }, }); expect(() => validateSLO(slo)).toThrowError('Invalid settings.sync_delay'); diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap index 6c472f3a3450a..9d231462c03af 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/reset_slo.test.ts.snap @@ -147,6 +147,7 @@ exports[`ResetSLO resets all associated resources 6`] = ` "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, @@ -410,6 +411,7 @@ exports[`ResetSLO resets all associated resources 9`] = ` "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap index 95b3ca924632e..671c5c13a39ea 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/__snapshots__/slo_definition_client.test.ts.snap @@ -36,6 +36,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts index 8f6bee0fe34c5..cefff32016e2b 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/create_slo.test.ts @@ -68,6 +68,7 @@ describe('CreateSLO', () => { settings: { syncDelay: oneMinute(), frequency: oneMinute(), + preventInitialBackfill: false, }, revision: 1, tags: [], @@ -108,6 +109,40 @@ describe('CreateSLO', () => { settings: { syncDelay: fiveMinute(), frequency: oneMinute(), + preventInitialBackfill: false, + }, + revision: 1, + tags: ['one', 'two'], + enabled: true, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + }), + { throwOnConflict: true } + ); + }); + + it('overrides the settings when provided', async () => { + const sloParams = createSLOParams({ + indicator: createAPMTransactionErrorRateIndicator(), + tags: ['one', 'two'], + settings: { + syncDelay: fiveMinute(), + frequency: fiveMinute(), + preventInitialBackfill: true, + }, + }); + mockTransformManager.install.mockResolvedValue('slo-transform-id'); + + await createSLO.execute(sloParams); + + expect(mockRepository.save).toHaveBeenCalledWith( + expect.objectContaining({ + ...sloParams, + id: expect.any(String), + settings: { + syncDelay: fiveMinute(), + frequency: fiveMinute(), + preventInitialBackfill: true, }, revision: 1, tags: ['one', 'two'], diff --git a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts index ce8258a222df3..60a5da93d9bf7 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/create_slo.ts @@ -127,6 +127,7 @@ export class CreateSLO { settings: { syncDelay: params.settings?.syncDelay ?? new Duration(1, DurationUnit.Minute), frequency: params.settings?.frequency ?? new Duration(1, DurationUnit.Minute), + preventInitialBackfill: params.settings?.preventInitialBackfill ?? false, }, revision: params.revision ?? 1, enabled: true, diff --git a/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts index 239b3aaaec518..183d4beee19dd 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/find_slo.test.ts @@ -80,6 +80,7 @@ describe('FindSLO', () => { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts b/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts index 74a548a2ad4c4..c290523512b9c 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/fixtures/slo.ts @@ -166,6 +166,7 @@ const defaultSLO: Omit { settings: { syncDelay: '1m', frequency: '1m', + preventInitialBackfill: false, }, summary: { status: 'HEALTHY', diff --git a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts index 1f8e1d415c484..9f300a148ac2e 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/slo_repository.ts @@ -138,6 +138,11 @@ export class KibanaSavedObjectsSLORepository implements SLORepository { // if not present, we considered the version to be 1, e.g. not migrated. // We would need to call the _reset api on this SLO. version: storedSLO.version ?? 1, + // settings.preventInitialBackfill was added in 8.15.0 + settings: { + ...storedSLO.settings, + preventInitialBackfill: storedSLO.settings?.preventInitialBackfill ?? false, + }, }); if (isLeft(result)) { diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts index 4a0ade1c9aa68..2ab6b26442967 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.test.ts @@ -150,4 +150,28 @@ describe('APM Transaction Duration Transform Generator', () => { expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createAPMTransactionDurationIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts index 7dde0e0a664e3..6bbec476754cb 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_duration.ts @@ -23,7 +23,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { APMTransactionDurationIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { parseIndex } from './common'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class ApmTransactionDurationTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -71,15 +71,7 @@ export class ApmTransactionDurationTransformGenerator extends TransformGenerator } private buildSource(slo: SLODefinition, indicator: APMTransactionDurationIndicator) { - const queryFilter: estypes.QueryDslQueryContainer[] = [ - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, - ]; + const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { queryFilter.push({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts index c9a85ae5df34e..5706521cdf2f6 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.test.ts @@ -6,7 +6,7 @@ */ import { ALL_VALUE } from '@kbn/slo-schema'; -import { twoMinute } from '../fixtures/duration'; +import { oneMinute, twoMinute } from '../fixtures/duration'; import { createAPMTransactionErrorRateIndicator, createSLO, @@ -153,4 +153,28 @@ describe('APM Transaction Error Rate Transform Generator', () => { expect(transform.source.query).toMatchSnapshot(); expect(transform.pivot?.group_by).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createAPMTransactionErrorRateIndicator(), + settings: { + frequency: oneMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-240s/m', // 1m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts index 99730152ea322..59b753711a0d8 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/apm_transaction_error_rate.ts @@ -21,8 +21,7 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { APMTransactionErrorRateIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; -import { parseIndex } from './common'; -import { getTimesliceTargetComparator } from './common'; +import { parseIndex, getTimesliceTargetComparator, getFilterRange } from './common'; export class ApmTransactionErrorRateTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -70,15 +69,7 @@ export class ApmTransactionErrorRateTransformGenerator extends TransformGenerato } private buildSource(slo: SLODefinition, indicator: APMTransactionErrorRateIndicator) { - const queryFilter: estypes.QueryDslQueryContainer[] = [ - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, - ]; + const queryFilter: estypes.QueryDslQueryContainer[] = [getFilterRange(slo, '@timestamp')]; if (indicator.params.service !== ALL_VALUE) { queryFilter.push({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts index baf02a9102f02..2e52a5442c12e 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.test.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { getTimesliceTargetComparator, parseIndex } from './common'; +import { fiveMinute, twoMinute } from '../fixtures/duration'; +import { createSLO } from '../fixtures/slo'; +import { thirtyDaysRolling } from '../fixtures/time_window'; +import { getTimesliceTargetComparator, parseIndex, getFilterRange } from './common'; describe('common', () => { describe('parseIndex', () => { @@ -30,4 +33,49 @@ describe('common', () => { expect(getTimesliceTargetComparator(0.000000001)).toBe('>='); }); }); + + describe('getFilterRange', () => { + it('starts at now (accounting for delay) when preventInitialBackfill is true', () => { + expect( + getFilterRange( + createSLO({ + settings: { + frequency: twoMinute(), + syncDelay: fiveMinute(), + preventInitialBackfill: true, + }, + }), + '@timestamp' + ) + ).toEqual({ + range: { + '@timestamp': { + gte: 'now-480s/m', + }, + }, + }); + }); + + it('starts at now minus the time window when preventInitialBackfill is false', () => { + expect( + getFilterRange( + createSLO({ + timeWindow: thirtyDaysRolling(), + settings: { + frequency: twoMinute(), + syncDelay: fiveMinute(), + preventInitialBackfill: false, + }, + }), + '@timestamp' + ) + ).toEqual({ + range: { + '@timestamp': { + gte: 'now-30d/d', + }, + }, + }); + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts index 12822d6b41e68..fd0a65d9c3887 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts @@ -6,7 +6,9 @@ */ import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import { QuerySchema, kqlQuerySchema } from '@kbn/slo-schema'; +import { kqlQuerySchema, QuerySchema } from '@kbn/slo-schema'; +import { SLODefinition } from '../../domain/models'; +import { getDelayInSecondsFromSLO } from '../../domain/services/get_delay_in_seconds_from_slo'; import { InvalidTransformError } from '../../errors'; export function getElasticsearchQueryOrThrow(kuery: QuerySchema = '') { @@ -39,3 +41,26 @@ export function parseIndex(index: string): string | string[] { export function getTimesliceTargetComparator(timesliceTarget: number) { return timesliceTarget === 0 ? '>' : '>='; } + +/** + * Use the settings.preventInitialBackfill flag to determine the range filter for the rollup transform + * preventInitialBackfill == true: we use the current time minus some buffer to account for the ingestion delay + * preventInitialBackfill === false: we use the time window duration to get the data for the last N days + */ +export function getFilterRange(slo: SLODefinition, timestampField: string) { + return slo.settings.preventInitialBackfill === true + ? { + range: { + [timestampField]: { + gte: `now-${getDelayInSecondsFromSLO(slo)}s/m`, + }, + }, + } + : { + range: { + [timestampField]: { + gte: `now-${slo.timeWindow.duration.format()}/d`, + }, + }, + }; +} diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts index 172d7c50cd59e..2307956564ba3 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.test.ts @@ -165,4 +165,28 @@ describe('Histogram Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createHistogramIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts index 57fbaa630d367..c8cfb52239f89 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/histogram.ts @@ -21,7 +21,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { GetHistogramIndicatorAggregation } from '../aggregations'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class HistogramTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -52,13 +52,7 @@ export class HistogramTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts index 7489ebd06c5f4..4b74695c8942e 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.test.ts @@ -121,4 +121,28 @@ describe('KQL Custom Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createKQLCustomIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts index fb52772b762cd..573a83a5400de 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/kql_custom.ts @@ -16,7 +16,7 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { KQLCustomIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export class KQLCustomTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition): TransformPutTransformRequest { @@ -47,13 +47,7 @@ export class KQLCustomTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts index 9ebacde28f0ed..1f6a1638baf1e 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { twoMinute } from '../fixtures/duration'; import { createMetricCustomIndicator, createSLO, @@ -215,4 +216,28 @@ describe('Metric Custom Transform Generator', () => { expect(transform.pivot!.aggregations!['slo.denominator']).toMatchSnapshot(); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createMetricCustomIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + log_timestamp: { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts index 9242e80b09270..d004b3e01d890 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/metric_custom.ts @@ -17,7 +17,7 @@ import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_tr import { MetricCustomIndicator, SLODefinition } from '../../domain/models'; import { InvalidTransformError } from '../../errors'; import { GetCustomMetricIndicatorAggregation } from '../aggregations'; -import { getTimesliceTargetComparator } from './common'; +import { getTimesliceTargetComparator, getFilterRange } from './common'; export const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; @@ -50,13 +50,7 @@ export class MetricCustomTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts index 78d6da1eb5bca..2a3a6f188eaa9 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.test.ts @@ -10,6 +10,7 @@ import { SLODefinition } from '../../domain/models'; import { createSLO, createSyntheticsAvailabilityIndicator } from '../fixtures/slo'; import { SyntheticsAvailabilityTransformGenerator } from './synthetics_availability'; import { SYNTHETICS_INDEX_PATTERN } from '../../../common/constants'; +import { twoMinute } from '../fixtures/duration'; const generator = new SyntheticsAvailabilityTransformGenerator(); @@ -404,4 +405,28 @@ describe('Synthetics Availability Transform Generator', () => { }, }); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLO({ + indicator: createSyntheticsAvailabilityIndicator(), + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo, 'default'); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-300s/m', // 2m + 2m + 60s + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts index ca820f524d7ef..a98f2a2b3e232 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/synthetics_availability.ts @@ -24,6 +24,8 @@ import { import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { InvalidTransformError } from '../../errors'; import { SLODefinition } from '../../domain/models'; +import { getFilterRange } from './common'; + export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator { public getTransformParams(slo: SLODefinition, spaceId: string): TransformPutTransformRequest { if (!syntheticsAvailabilityIndicatorSchema.is(slo.indicator)) { @@ -108,13 +110,7 @@ export class SyntheticsAvailabilityTransformGenerator extends TransformGenerator const queryFilter: estypes.QueryDslQueryContainer[] = [ { term: { 'summary.final_attempt': true } }, { term: { 'meta.space_id': spaceId } }, - { - range: { - '@timestamp': { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, '@timestamp'), ]; const { monitorIds, tags, projects } = buildParamValues({ monitorIds: indicator.params.monitorIds || [], diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts index 7d221c25c27ed..1fcd8c6abe892 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { twoMinute } from '../fixtures/duration'; import { createTimesliceMetricIndicator, createSLOWithTimeslicesBudgetingMethod, @@ -164,4 +165,28 @@ describe('Timeslice Metric Transform Generator', () => { }, }); }); + + it("overrides the range filter when 'preventInitialBackfill' is true", () => { + const slo = createSLOWithTimeslicesBudgetingMethod({ + indicator: everythingIndicator, + settings: { + frequency: twoMinute(), + syncDelay: twoMinute(), + preventInitialBackfill: true, + }, + }); + + const transform = generator.getTransformParams(slo); + + // @ts-ignore + const rangeFilter = transform.source.query.bool.filter.find((f) => 'range' in f); + + expect(rangeFilter).toEqual({ + range: { + '@timestamp': { + gte: 'now-360s/m', // 2m + 2m + 2m slice window + }, + }, + }); + }); }); diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts index 678dc2f4d76d8..719766b0c4efe 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/timeslice_metric.ts @@ -12,7 +12,6 @@ import { timesliceMetricIndicatorSchema, timeslicesBudgetingMethodSchema, } from '@kbn/slo-schema'; - import { InvalidTransformError } from '../../errors'; import { getSLOTransformTemplate } from '../../assets/transform_templates/slo_transform_template'; import { getElasticsearchQueryOrThrow, parseIndex, TransformGenerator } from '.'; @@ -23,6 +22,7 @@ import { } from '../../../common/constants'; import { SLODefinition } from '../../domain/models'; import { GetTimesliceMetricIndicatorAggregation } from '../aggregations'; +import { getFilterRange } from './common'; const INVALID_EQUATION_REGEX = /[^A-Z|+|\-|\s|\d+|\.|\(|\)|\/|\*|>|<|=|\?|\:|&|\!|\|]+/g; @@ -55,13 +55,7 @@ export class TimesliceMetricTransformGenerator extends TransformGenerator { query: { bool: { filter: [ - { - range: { - [indicator.params.timestampField]: { - gte: `now-${slo.timeWindow.duration.format()}/d`, - }, - }, - }, + getFilterRange(slo, indicator.params.timestampField), getElasticsearchQueryOrThrow(indicator.params.filter), ], }, diff --git a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap index 153c007adcfa1..4825ed65e4fc9 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap +++ b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/__snapshots__/remote_summary_doc_to_slo.test.ts.snap @@ -32,6 +32,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, @@ -81,6 +82,7 @@ Object { "unit": "m", "value": 1, }, + "preventInitialBackfill": false, "syncDelay": Duration { "unit": "m", "value": 1, diff --git a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts index d63194c871139..0d98a021f505f 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/unsafe_federated/remote_summary_doc_to_slo.ts @@ -29,7 +29,12 @@ export function fromRemoteSummaryDocumentToSloDefinition( timesliceTarget: summaryDoc.slo.objective.timesliceTarget ?? undefined, timesliceWindow: summaryDoc.slo.objective.timesliceWindow ?? undefined, }, - settings: { syncDelay: '1m', frequency: '1m' }, + settings: { + syncDelay: '1m', + frequency: '1m', + // added in 8.15.0 + preventInitialBackfill: false, + }, revision: summaryDoc.slo.revision, enabled: true, tags: summaryDoc.slo.tags, diff --git a/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts b/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts index e520b952128fd..974f72561aeff 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/update_slo.test.ts @@ -192,7 +192,11 @@ describe('UpdateSLO', () => { const slo = createSLO(); mockRepository.findById.mockResolvedValueOnce(slo); - const newSettings = { ...slo.settings, timestamp_field: 'newField' }; + const newSettings = { + ...slo.settings, + frequency: fiveMinute(), + preventInitialBackfill: true, + }; await updateSLO.execute(slo.id, { settings: newSettings }); expectDeletionOfOriginalSLOResources(slo); diff --git a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts index a55f4b2ce9fca..5c8812cd4a886 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/update_slo.ts @@ -39,6 +39,7 @@ export class UpdateSLO { const originalSlo = await this.repository.findById(sloId); let updatedSlo: SLODefinition = Object.assign({}, originalSlo, params, { groupBy: !!params.groupBy ? params.groupBy : originalSlo.groupBy, + settings: mergePartialSettings(originalSlo.settings, params.settings), }); if (isEqual(originalSlo, updatedSlo)) { @@ -183,3 +184,13 @@ export class UpdateSLO { return updateSLOResponseSchema.encode(slo); } } + +/** + * Settings are merged by overwriting the original settings with the optional new partial settings. + */ +function mergePartialSettings( + originalSettings: SLODefinition['settings'], + newPartialSettings: UpdateSLOParams['settings'] +) { + return Object.assign({}, originalSettings, newPartialSettings); +} diff --git a/x-pack/test/api_integration/apis/slos/create_slo.ts b/x-pack/test/api_integration/apis/slos/create_slo.ts index c61de233fb423..72735e7f668bb 100644 --- a/x-pack/test/api_integration/apis/slos/create_slo.ts +++ b/x-pack/test/api_integration/apis/slos/create_slo.ts @@ -90,6 +90,7 @@ export default function ({ getService }: FtrProviderContext) { settings: { frequency: '1m', syncDelay: '1m', + preventInitialBackfill: false, }, tags: ['test'], timeWindow: { diff --git a/x-pack/test/api_integration/apis/slos/update_slo.ts b/x-pack/test/api_integration/apis/slos/update_slo.ts index 4d920895ffcc6..ebddd16c6508f 100644 --- a/x-pack/test/api_integration/apis/slos/update_slo.ts +++ b/x-pack/test/api_integration/apis/slos/update_slo.ts @@ -94,6 +94,7 @@ export default function ({ getService }: FtrProviderContext) { settings: { frequency: '1m', syncDelay: '1m', + preventInitialBackfill: false, }, tags: ['test'], timeWindow: { From 2903a7fdabc34448394e0bac7c5ab39e571670ef Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 28 May 2024 15:58:51 +0100 Subject: [PATCH 38/59] skip flaky suite (#184360) --- .../rule_management/rule_details/execution_log.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts index 77c88c0b8573b..219f61b655f5d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_details/execution_log.cy.ts @@ -22,7 +22,8 @@ import { getNewRule } from '../../../../objects/rule'; import { EXECUTION_SHOWING } from '../../../../screens/rule_details'; import { manualRuleRun } from '../../../../tasks/api_calls/backfill'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/184360 +describe.skip( 'Event log', { tags: ['@ess', '@serverless'], From a7601a7992402d781b9b0627be1bf660f5b8ad67 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 28 May 2024 11:02:23 -0400 Subject: [PATCH 39/59] skip failing test suite (#184310) --- .../apis/epm/install_hidden_datastreams.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/fleet_api_integration/apis/epm/install_hidden_datastreams.ts b/x-pack/test/fleet_api_integration/apis/epm/install_hidden_datastreams.ts index 2ec6fb92000e3..ca971aa006954 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/install_hidden_datastreams.ts +++ b/x-pack/test/fleet_api_integration/apis/epm/install_hidden_datastreams.ts @@ -19,7 +19,8 @@ export default function (providerContext: FtrProviderContext) { await supertest.delete(`/api/fleet/epm/packages/${name}/${version}`).set('kbn-xsrf', 'xxxx'); }; - describe('installing with hidden datastream', async () => { + // Failing: See https://github.com/elastic/kibana/issues/184310 + describe.skip('installing with hidden datastream', async () => { skipIfNoDockerRegistry(providerContext); setupFleetAndAgents(providerContext); From 9c75ea1fa79b7d5fdc3e78befb7bd199a6a35953 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 28 May 2024 10:25:21 -0500 Subject: [PATCH 40/59] [search] Create scripted field sort DSL without looking at the field list (#183955) ## Summary tldr; Less field loading Previously, this code would look at the field list to determine whether a scripted field was involved necessitating special handling. This is unnecessary because we could simply look for scripted or runtime fields and determine the necessary output without a hit against the field_caps api. ~~Of note, I removed a check to see if a given scripted field is sortable. All supported scripted field types are sortable.~~ Due to a bug, date type scripted fields aren't sortable. This PR prevents sorting based on a date type scripted field. https://github.com/elastic/kibana/issues/75711 --------- Co-authored-by: Matthias Wilhelm --- .../src/__mocks__/data_view.ts | 4 +++ .../data_table_columns.test.tsx.snap | 28 +++++++++++++++ .../normalize_sort_request.test.ts | 34 +++++-------------- .../search_source/normalize_sort_request.ts | 10 ++++-- .../common/data_views/abstract_data_views.ts | 20 +++++++++++ .../common/data_views/data_view_lazy.ts | 4 +-- .../common/fields/data_view_field.test.ts | 3 ++ .../common/fields/data_view_field.ts | 4 +++ 8 files changed, 76 insertions(+), 31 deletions(-) diff --git a/packages/kbn-discover-utils/src/__mocks__/data_view.ts b/packages/kbn-discover-utils/src/__mocks__/data_view.ts index b7a365c8b674c..73828c077b971 100644 --- a/packages/kbn-discover-utils/src/__mocks__/data_view.ts +++ b/packages/kbn-discover-utils/src/__mocks__/data_view.ts @@ -122,6 +122,10 @@ export const buildDataViewMock = ({ getTimeField: () => { return dataViewFields.find((field) => field.name === timeFieldName); }, + getScriptedField: () => { + return dataViewFields.find((field) => field.name === timeFieldName); + }, + getRuntimeField: () => null, } as unknown as DataView; dataView.isTimeBased = () => !!timeFieldName; diff --git a/packages/kbn-unified-data-table/src/components/__snapshots__/data_table_columns.test.tsx.snap b/packages/kbn-unified-data-table/src/components/__snapshots__/data_table_columns.test.tsx.snap index f7c30484bfc52..eee2df60fa1d2 100644 --- a/packages/kbn-unified-data-table/src/components/__snapshots__/data_table_columns.test.tsx.snap +++ b/packages/kbn-unified-data-table/src/components/__snapshots__/data_table_columns.test.tsx.snap @@ -311,6 +311,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -555,6 +557,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -811,6 +815,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -1054,6 +1060,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -1342,6 +1350,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -1623,6 +1633,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -1786,6 +1798,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -1941,6 +1955,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -2143,6 +2159,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -2357,6 +2375,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -2557,6 +2577,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -2759,6 +2781,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -2998,6 +3022,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", @@ -3215,6 +3241,8 @@ Array [ "getFormatterForField": [MockFunction], "getIndexPattern": [Function], "getName": [Function], + "getRuntimeField": [Function], + "getScriptedField": [Function], "getSourceFiltering": [Function], "getTimeField": [Function], "id": "index-pattern-with-timefield-id", diff --git a/src/plugins/data/common/search/search_source/normalize_sort_request.test.ts b/src/plugins/data/common/search/search_source/normalize_sort_request.test.ts index 265d8c085b7e9..98c687b015995 100644 --- a/src/plugins/data/common/search/search_source/normalize_sort_request.test.ts +++ b/src/plugins/data/common/search/search_source/normalize_sort_request.test.ts @@ -29,14 +29,15 @@ describe('SearchSource#normalizeSortRequest', function () { name: 'script boolean', type: 'boolean', }; - const murmurScriptedField = { - ...scriptedField, - sortable: false, - name: 'murmur script', - type: 'murmur3', - }; + + const fields = [scriptedField, stringScriptedField, booleanScriptedField]; + const indexPattern = { - fields: [scriptedField, stringScriptedField, booleanScriptedField, murmurScriptedField], + fields, + getScriptedField: (name: string) => { + return fields.find((field) => field.name === name); + }, + getRuntimeField: (name: string) => null, } as DataView; it('should return an array', function () { @@ -153,25 +154,6 @@ describe('SearchSource#normalizeSortRequest', function () { ]); }); - it('should use script based sorting only on sortable types', function () { - const result = normalizeSortRequest( - [ - { - [murmurScriptedField.name]: SortDirection.asc, - }, - ], - indexPattern - ); - - expect(result).toEqual([ - { - [murmurScriptedField.name]: { - order: SortDirection.asc, - }, - }, - ]); - }); - it('should remove unmapped_type parameter from _score sorting', function () { const result = normalizeSortRequest({ _score: SortDirection.desc }, indexPattern, { unmapped_type: 'boolean', diff --git a/src/plugins/data/common/search/search_source/normalize_sort_request.ts b/src/plugins/data/common/search/search_source/normalize_sort_request.ts index 49cdd8ba289e3..bc2d9c59b7185 100644 --- a/src/plugins/data/common/search/search_source/normalize_sort_request.ts +++ b/src/plugins/data/common/search/search_source/normalize_sort_request.ts @@ -41,9 +41,13 @@ function normalize( const [[sortField, sortOrder]] = Object.entries(sortable); const order = typeof sortOrder === 'object' ? sortOrder : { order: sortOrder }; - if (indexPattern && typeof indexPattern !== 'string') { - const indexField = indexPattern.fields.find(({ name }) => name === sortField); - if (indexField && indexField.scripted && indexField.sortable) { + if ( + indexPattern && + typeof indexPattern !== 'string' && + !indexPattern.getRuntimeField(sortField) + ) { + const indexField = indexPattern.getScriptedField(sortField); + if (indexField && indexField.scripted && indexField.type !== 'date') { return { _script: { script: { diff --git a/src/plugins/data_views/common/data_views/abstract_data_views.ts b/src/plugins/data_views/common/data_views/abstract_data_views.ts index 18fcaf5331d76..42d1b5feb0d61 100644 --- a/src/plugins/data_views/common/data_views/abstract_data_views.ts +++ b/src/plugins/data_views/common/data_views/abstract_data_views.ts @@ -442,6 +442,26 @@ export abstract class AbstractDataView { return this.upsertScriptedFieldInternal(field); } + /** + * Only used by search source to process sorting of scripted fields + * @param name field name + * @returns DataViewFieldBase + */ + getScriptedField(name: string): DataViewFieldBase | undefined { + // runtime fields override scripted fields + if (this.runtimeFieldMap[name]) { + return; + } + + const field = this.scriptedFieldsMap[name]; + if (field) { + return { + ...field, + scripted: true, + }; + } + } + /** * Checks if runtime field exists * @param name field name diff --git a/src/plugins/data_views/common/data_views/data_view_lazy.ts b/src/plugins/data_views/common/data_views/data_view_lazy.ts index e524ad2cbf2f3..a6aee7a33f3ba 100644 --- a/src/plugins/data_views/common/data_views/data_view_lazy.ts +++ b/src/plugins/data_views/common/data_views/data_view_lazy.ts @@ -391,8 +391,8 @@ export class DataViewLazy extends AbstractDataView { fld = new DataViewField({ ...field, scripted: true, - searchable: false, - aggregatable: false, + searchable: true, + aggregatable: true, count: this.fieldAttrs?.[field.name]?.count, customLabel: this.fieldAttrs?.[field.name]?.customLabel, customDescription: this.fieldAttrs?.[field.name]?.customDescription, diff --git a/src/plugins/data_views/common/fields/data_view_field.test.ts b/src/plugins/data_views/common/fields/data_view_field.test.ts index 315758a2007e1..888a0f85104ac 100644 --- a/src/plugins/data_views/common/fields/data_view_field.test.ts +++ b/src/plugins/data_views/common/fields/data_view_field.test.ts @@ -129,6 +129,9 @@ describe('Field', function () { const fieldC = getField({ indexed: false, aggregatable: false, scripted: false }); expect(fieldC.sortable).toEqual(false); + + const fieldD = getField({ type: 'date', scripted: true }); + expect(fieldD.sortable).toEqual(false); }); it('calculates filterable', () => { diff --git a/src/plugins/data_views/common/fields/data_view_field.ts b/src/plugins/data_views/common/fields/data_view_field.ts index 8536244520792..a5bc45adeb345 100644 --- a/src/plugins/data_views/common/fields/data_view_field.ts +++ b/src/plugins/data_views/common/fields/data_view_field.ts @@ -298,6 +298,10 @@ export class DataViewField implements DataViewFieldBase { * Returns true if field is sortable */ public get sortable() { + if (this.scripted && this.spec.type === 'date') { + return false; + } + return ( this.name === '_score' || ((this.spec.indexed || this.aggregatable) && this.kbnFieldType.sortable) From 04d132f886ddc59eb7d520740b0f67d311faa64c Mon Sep 17 00:00:00 2001 From: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Date: Tue, 28 May 2024 16:49:18 +0100 Subject: [PATCH 41/59] [Security Solution][Detection engine] fixes flaky new terms suppression test (#184350) ## Summary - addresses https://github.com/elastic/kibana/issues/184220 --- .../execution_logic/esql_suppression.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts index f7264e064bdba..7ab53b9817970 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/esql_suppression.ts @@ -1014,7 +1014,7 @@ export default ({ getService }: FtrProviderContext) => { previewId, }); // lodash sortBy is used here because custom_field is non ECS and not mapped in alerts index, so can't be sorted by - const sortedAlerts = sortBy(previewAlerts, 'custom_field'); + const sortedAlerts = sortBy(previewAlerts, '_source.custom_field'); expect(previewAlerts.length).toEqual(2); expect(sortedAlerts[0]._source).toEqual({ @@ -1112,7 +1112,7 @@ export default ({ getService }: FtrProviderContext) => { previewId, }); // lodash sortBy is used here because custom_field is non ECS and not mapped in alerts index, so can't be sorted by - const sortedAlerts = sortBy(previewAlerts, 'custom_field'); + const sortedAlerts = sortBy(previewAlerts, '_source.custom_field'); expect(previewAlerts.length).toEqual(2); expect(sortedAlerts[0]._source).toEqual({ From 4f2f30f52d34ae5f3714cc15f18c10c813e58cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Tue, 28 May 2024 17:00:04 +0100 Subject: [PATCH 42/59] [Stateful sidenav] Put recently accessed in footer for all solutions (#184298) --- packages/solution-nav/oblt/definition.ts | 2 +- x-pack/plugins/enterprise_search/public/navigation_tree.ts | 2 +- .../public/navigation/side_navigation.ts | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/solution-nav/oblt/definition.ts b/packages/solution-nav/oblt/definition.ts index 80d926b43ce64..d8721e52a28db 100644 --- a/packages/solution-nav/oblt/definition.ts +++ b/packages/solution-nav/oblt/definition.ts @@ -19,7 +19,6 @@ const icon = 'logoObservability'; const navTree: NavigationTreeDefinition = { body: [ - { type: 'recentlyAccessed' }, { type: 'navGroup', id: 'observability_project_nav', @@ -232,6 +231,7 @@ const navTree: NavigationTreeDefinition = { }, ], footer: [ + { type: 'recentlyAccessed' }, { type: 'navItem', title: i18n.translate('navigation.obltNav.getStarted', { diff --git a/x-pack/plugins/enterprise_search/public/navigation_tree.ts b/x-pack/plugins/enterprise_search/public/navigation_tree.ts index 310a0eb24b263..9dc1d324fd118 100644 --- a/x-pack/plugins/enterprise_search/public/navigation_tree.ts +++ b/x-pack/plugins/enterprise_search/public/navigation_tree.ts @@ -80,7 +80,6 @@ export const getNavigationTreeDefinition = ({ map(({ indices, searchApps, collections }) => { const navTree: NavigationTreeDefinition = { body: [ - { type: 'recentlyAccessed' }, { breadcrumbStatus: 'hidden', children: [ @@ -242,6 +241,7 @@ export const getNavigationTreeDefinition = ({ }, ], footer: [ + { type: 'recentlyAccessed' }, { breadcrumbStatus: 'hidden', children: [ diff --git a/x-pack/plugins/security_solution_ess/public/navigation/side_navigation.ts b/x-pack/plugins/security_solution_ess/public/navigation/side_navigation.ts index 68caabdd988fb..d72d22a7a79e0 100644 --- a/x-pack/plugins/security_solution_ess/public/navigation/side_navigation.ts +++ b/x-pack/plugins/security_solution_ess/public/navigation/side_navigation.ts @@ -28,9 +28,12 @@ export const initSideNavigation = async (services: Services) => { const essNavigationTree$ = navigationTree$.pipe( map((navigationTree) => produce(navigationTree, (draft) => { + if (draft.footer) { + draft.footer.unshift({ type: 'recentlyAccessed' }); + } const footerGroup: GroupDefinition | undefined = draft.footer?.find( - ({ type }) => type === 'navGroup' - ) as GroupDefinition; + (node): node is GroupDefinition => node.type === 'navGroup' + ); const management = footerGroup?.children.find((child) => child.link === 'management'); if (management) { management.renderAs = 'panelOpener'; From cf1ff97c1248d7c7fdb281b29f22477e20603378 Mon Sep 17 00:00:00 2001 From: Drew Tate Date: Tue, 28 May 2024 10:02:20 -0600 Subject: [PATCH 43/59] [ES|QL] add function definition sync to CI (#184225) ## Summary Runs the script added in https://github.com/elastic/kibana/pull/179584 on the weekly schedule. Successful run: https://buildkite.com/elastic/kibana-es-ql-grammar-sync/builds/21#018fab3b-d051-4852-a03e-889cf156fb70/838 --------- Co-authored-by: Stratoula Kalafateli --- .buildkite/pipelines/esql_grammar_sync.yml | 11 ++- .../esql_generate_function_definitions.sh | 76 +++++++++++++++++++ .github/CODEOWNERS | 1 + .../scripts/generate_function_definitions.ts | 2 - 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100755 .buildkite/scripts/steps/esql_generate_function_definitions.sh diff --git a/.buildkite/pipelines/esql_grammar_sync.yml b/.buildkite/pipelines/esql_grammar_sync.yml index ffa532101cf3c..2bf14af24b20e 100644 --- a/.buildkite/pipelines/esql_grammar_sync.yml +++ b/.buildkite/pipelines/esql_grammar_sync.yml @@ -1,6 +1,15 @@ steps: - command: .buildkite/scripts/steps/esql_grammar_sync.sh - label: ES|QL Grammar Sync + label: Grammar Sync + timeout_in_minutes: 10 + agents: + image: family/kibana-ubuntu-2004 + imageProject: elastic-images-prod + provider: gcp + machineType: n2-standard-2 + preemptible: true + - command: .buildkite/scripts/steps/esql_generate_function_definitions.sh + label: Generate Function Definitions timeout_in_minutes: 10 agents: image: family/kibana-ubuntu-2004 diff --git a/.buildkite/scripts/steps/esql_generate_function_definitions.sh b/.buildkite/scripts/steps/esql_generate_function_definitions.sh new file mode 100755 index 0000000000000..ecb5b996df4c2 --- /dev/null +++ b/.buildkite/scripts/steps/esql_generate_function_definitions.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +report_main_step () { + echo "--- $1" +} + +main () { + cd "$PARENT_DIR" + + report_main_step "Cloning Elasticsearch repository" + + rm -rf elasticsearch + git clone https://github.com/elastic/elasticsearch --depth 1 + + report_main_step "Bootstrapping Kibana" + + cd "$KIBANA_DIR" + + .buildkite/scripts/bootstrap.sh + + cd "$KIBANA_DIR/packages/kbn-esql-validation-autocomplete" + + report_main_step "Generate function definitions" + + yarn make:defs $PARENT_DIR/elasticsearch + + report_main_step "Generate function validation tests" + + yarn make:tests + + # Check for differences + set +e + git diff --exit-code --quiet . + if [ $? -eq 0 ]; then + echo "No differences found. Our work is done here." + exit + fi + set -e + + report_main_step "Differences found. Checking for an existing pull request." + + KIBANA_MACHINE_USERNAME="kibanamachine" + git config --global user.name "$KIBANA_MACHINE_USERNAME" + git config --global user.email '42973632+kibanamachine@users.noreply.github.com' + + PR_TITLE='[ES|QL] Update function definitions' + PR_BODY='This PR updates the function definitions based on the latest metadata from Elasticsearch.' + + # Check if a PR already exists + pr_search_result=$(gh pr list --search "$PR_TITLE" --state open --author "$KIBANA_MACHINE_USERNAME" --limit 1 --json title -q ".[].title") + + if [ "$pr_search_result" == "$PR_TITLE" ]; then + echo "PR already exists. Exiting." + exit + fi + + echo "No existing PR found. Committing changes." + + # Make a commit + BRANCH_NAME="esql_generate_function_definitions_$(date +%s)" + + git checkout -b "$BRANCH_NAME" + + git add ./**/* + git commit -m "Update function definitions" + + report_main_step "Changes committed. Creating pull request." + + git push origin "$BRANCH_NAME" + + # Create a PR + gh pr create --title "$PR_TITLE" --body "$PR_BODY" --base main --head "${BRANCH_NAME}" --label 'release_note:skip' --label 'Team:ESQL' +} + +main diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3d9ca954ecb7d..7bb36681643aa 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1219,6 +1219,7 @@ x-pack/test/observability_ai_assistant_functional @elastic/obs-ai-assistant /WORKSPACE.bazel @elastic/kibana-operations /.buildkite/ @elastic/kibana-operations /.buildkite/scripts/steps/esql_grammar_sync.sh @elastic/kibana-esql +/.buildkite/scripts/steps/esql_generate_function_definitions.sh @elastic/kibana-esql /.buildkite/pipelines/esql_grammar_sync.yml @elastic/kibana-esql /kbn_pm/ @elastic/kibana-operations /x-pack/dev-tools @elastic/kibana-operations diff --git a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts index b7cd905c63baf..65e2c8fe38526 100644 --- a/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts +++ b/packages/kbn-esql-validation-autocomplete/scripts/generate_function_definitions.ts @@ -354,7 +354,6 @@ import type { FunctionDefinition } from './types'; const pathToElasticsearch = process.argv[2]; const ESFunctionDefinitionsDirectory = join( - __dirname, pathToElasticsearch, 'docs/reference/esql/functions/kibana/definition' ); @@ -365,7 +364,6 @@ import type { FunctionDefinition } from './types'; ); const evalFunctionDefinitions: FunctionDefinition[] = []; - // const aggFunctionDefinitions = []; for (const ESDefinition of ESFunctionDefinitions) { if (aliases.has(ESDefinition.name) || excludedFunctions.has(ESDefinition.name)) { continue; From ad27105af8d148633114473be0d15a64fdd08096 Mon Sep 17 00:00:00 2001 From: Carlos Crespo Date: Tue, 28 May 2024 18:16:42 +0200 Subject: [PATCH 44/59] [APM\ Update index template names considered in the diagnostics page (#184343) closes [#184333](https://github.com/elastic/kibana/issues/184333) ## Summary This PR updates the index template names that the diagnostics page verifies the existence of. After the changes made in the [APM package](https://github.com/elastic/integrations/pull/9949), to favor APM indices and ingest pipelines configured by elasticsearch, the index templates now have a `@template` suffix. eg: `metrics-apm.service_destination.1m` is now `metrics-apm.service_destination.1m@template` image image **When no indices are found** image ### How to test - Start a local ES and kibana instance - navigate to `/apm/diagnostics/index-templates?rangeFrom=now-15m&rangeTo=now` --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../bundle/get_existing_index_templates.ts | 10 ++++--- .../get_index_templates_by_index_pattern.ts | 13 +++++---- .../diagnostics/bundle/get_indices_states.ts | 10 ++++--- .../helpers/get_apm_index_template_names.ts | 29 ++++++++++++------- .../diagnostics/index_pattern_settings.ts | 4 ++- .../tests/diagnostics/index_templates.spec.ts | 4 ++- 6 files changed, 44 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_existing_index_templates.ts b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_existing_index_templates.ts index f217f72953ca5..228be70286595 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_existing_index_templates.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_existing_index_templates.ts @@ -19,10 +19,12 @@ export async function getExistingApmIndexTemplates({ }) { const apmIndexTemplateNames = getApmIndexTemplateNames(); const values = await Promise.all( - apmIndexTemplateNames.map(async (indexTemplateName) => { - const res = await getIndexTemplate(esClient, { name: indexTemplateName }); - return res.index_templates[0]; - }) + Object.values(apmIndexTemplateNames) + .flat() + .map(async (indexTemplateName) => { + const res = await getIndexTemplate(esClient, { name: indexTemplateName }); + return res.index_templates[0]; + }) ); return values.filter((v) => v !== undefined); diff --git a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_index_templates_by_index_pattern.ts b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_index_templates_by_index_pattern.ts index 0d9708344c370..6d87c98ea5e22 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_index_templates_by_index_pattern.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_index_templates_by_index_pattern.ts @@ -79,12 +79,13 @@ async function getTemplatePriority(esClient: ElasticsearchClient, name: string) function getIsNonStandardIndexTemplate(templateName: string) { const apmIndexTemplateNames = getApmIndexTemplateNames(); const stackIndexTemplateNames = ['logs', 'metrics']; - const isNonStandard = [...apmIndexTemplateNames, ...stackIndexTemplateNames].every( - (apmIndexTemplateName) => { - const notMatch = templateName !== apmIndexTemplateName; - return notMatch; - } - ); + const isNonStandard = [ + ...Object.values(apmIndexTemplateNames).flat(), + ...stackIndexTemplateNames, + ].every((apmIndexTemplateName) => { + const notMatch = templateName !== apmIndexTemplateName; + return notMatch; + }); return isNonStandard; } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_indices_states.ts b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_indices_states.ts index 8e3e25a3ebae0..aa8e2ef94d805 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_indices_states.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/bundle/get_indices_states.ts @@ -85,8 +85,10 @@ export function validateIngestPipelineName( } const indexTemplateNames = getApmIndexTemplateNames(); - return indexTemplateNames.some( - (indexTemplateName) => - dataStream.startsWith(indexTemplateName) && ingestPipelineId.startsWith(indexTemplateName) - ); + return Object.values(indexTemplateNames) + .flat() + .some( + (indexTemplateName) => + dataStream.startsWith(indexTemplateName) && ingestPipelineId.startsWith(indexTemplateName) + ); } diff --git a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/helpers/get_apm_index_template_names.ts b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/helpers/get_apm_index_template_names.ts index 23ec0c969821e..2a5f421ce67d9 100644 --- a/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/helpers/get_apm_index_template_names.ts +++ b/x-pack/plugins/observability_solution/apm/server/routes/diagnostics/helpers/get_apm_index_template_names.ts @@ -7,6 +7,7 @@ import { IndicesGetIndexTemplateIndexTemplateItem } from '@elastic/elasticsearch/lib/api/types'; +const suffix = 'template'; export function getApmIndexTemplateNames() { const indexTemplateNames = [ 'logs-apm.app', @@ -27,22 +28,30 @@ export function getApmIndexTemplateNames() { ].map((ds) => `${ds}.${interval}`); }); - return [...indexTemplateNames, ...rollupIndexTemplateNames]; + // For retrocompatibility, it returns index template names both pre and post APM integration package v8.15.0 + return [...indexTemplateNames, ...rollupIndexTemplateNames].reduce((acc, indexTemplateName) => { + acc[indexTemplateName] = [indexTemplateName, `${indexTemplateName}@${suffix}`]; + return acc; + }, {} as Record); } export function getApmIndexTemplates( existingIndexTemplates: IndicesGetIndexTemplateIndexTemplateItem[] ) { const apmIndexTemplateNames = getApmIndexTemplateNames(); - const standardIndexTemplates = apmIndexTemplateNames.map((templateName) => { - const matchingTemplate = existingIndexTemplates.find(({ name }) => name === templateName); - - return { - name: templateName, - exists: Boolean(matchingTemplate), - isNonStandard: false, - }; - }); + const standardIndexTemplates = Object.entries(apmIndexTemplateNames).map( + ([baseTemplateName, validIndexTemplateNames]) => { + const matchingTemplate = validIndexTemplateNames.find((templateName) => + existingIndexTemplates.find(({ name }) => name === templateName) + ); + + return { + name: matchingTemplate ?? baseTemplateName, + exists: Boolean(matchingTemplate), + isNonStandard: false, + }; + } + ); const nonStandardIndexTemplates = existingIndexTemplates .filter( diff --git a/x-pack/test/apm_api_integration/tests/diagnostics/index_pattern_settings.ts b/x-pack/test/apm_api_integration/tests/diagnostics/index_pattern_settings.ts index 64749066a2e04..d0ba7b1850d31 100644 --- a/x-pack/test/apm_api_integration/tests/diagnostics/index_pattern_settings.ts +++ b/x-pack/test/apm_api_integration/tests/diagnostics/index_pattern_settings.ts @@ -24,7 +24,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('When there is no data', () => { before(async () => { // delete APM index templates - await es.indices.deleteIndexTemplate({ name: getApmIndexTemplateNames() }); + await es.indices.deleteIndexTemplate({ + name: Object.values(getApmIndexTemplateNames()).flat(), + }); }); it('returns the built-in (non-APM) index templates`', async () => { diff --git a/x-pack/test/apm_api_integration/tests/diagnostics/index_templates.spec.ts b/x-pack/test/apm_api_integration/tests/diagnostics/index_templates.spec.ts index 2ece2835b5944..5c94de56abb30 100644 --- a/x-pack/test/apm_api_integration/tests/diagnostics/index_templates.spec.ts +++ b/x-pack/test/apm_api_integration/tests/diagnostics/index_templates.spec.ts @@ -24,7 +24,9 @@ export default function ApiTest({ getService }: FtrProviderContext) { describe('When there is no data', () => { before(async () => { // delete APM index templates - await es.indices.deleteIndexTemplate({ name: getApmIndexTemplateNames() }); + await es.indices.deleteIndexTemplate({ + name: Object.values(getApmIndexTemplateNames()).flat(), + }); }); it('verifies that none of the default APM index templates exists`', async () => { From 49925a3c4e1d7f298b965df988ba8c4880de72b6 Mon Sep 17 00:00:00 2001 From: "Eyo O. Eyo" <7893459+eokoneyo@users.noreply.github.com> Date: Tue, 28 May 2024 18:21:39 +0200 Subject: [PATCH 45/59] [Banner] Fix can't update the banner text from advanced settings (#184359) ## Summary Closes https://github.com/elastic/kibana/issues/184199 , by restricting the attempt to check that valid JSON was provided so it only happens when the expectation of the current field type is JSON. ## How to test - Set a banner using advanced settings. Save it. Reload the page. Confirm the banner is displayed - Try to edit or remove the banner message in the editor. - The save button should appear with no errors in the console --- .../components/field_input/input/code_editor_input.tsx | 5 +++-- .../field_input/input/markdown_editor_input.test.tsx | 10 +++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/kbn-management/settings/components/field_input/input/code_editor_input.tsx b/packages/kbn-management/settings/components/field_input/input/code_editor_input.tsx index 295425006d8ca..b20f51d208feb 100644 --- a/packages/kbn-management/settings/components/field_input/input/code_editor_input.tsx +++ b/packages/kbn-management/settings/components/field_input/input/code_editor_input.tsx @@ -54,12 +54,13 @@ export const CodeEditorInput = ({ const updateValue = useCallback( async (newValue: string, onUpdateFn) => { - const isJsonArray = Array.isArray(JSON.parse(defaultValue || '{}')); - const parsedValue = newValue || (isJsonArray ? '[]' : '{}'); + let parsedValue; // Validate JSON syntax if (field.type === 'json') { try { + const isJsonArray = Array.isArray(JSON.parse(defaultValue || 'null')); + parsedValue = newValue || (isJsonArray ? '[]' : '{}'); JSON.parse(parsedValue); } catch (e) { onUpdateFn({ diff --git a/packages/kbn-management/settings/components/field_input/input/markdown_editor_input.test.tsx b/packages/kbn-management/settings/components/field_input/input/markdown_editor_input.test.tsx index f26e59e43efbd..534429d7e2bf9 100644 --- a/packages/kbn-management/settings/components/field_input/input/markdown_editor_input.test.tsx +++ b/packages/kbn-management/settings/components/field_input/input/markdown_editor_input.test.tsx @@ -71,11 +71,15 @@ describe('MarkdownEditorInput', () => { const input = getByTestId(`${TEST_SUBJ_PREFIX_FIELD}-${id}`); fireEvent.change(input, { target: { value: '# New Markdown Title' } }); - await waitFor(() => + await waitFor(() => { + expect(defaultProps.onInputChange).not.toHaveBeenCalledWith({ + error: expect.any(String), + }); + expect(defaultProps.onInputChange).toHaveBeenCalledWith({ type: 'markdown', unsavedValue: '# New Markdown Title', - }) - ); + }); + }); }); }); From ef7677341b39e762d30aeeabd1ab6e163bd4668c Mon Sep 17 00:00:00 2001 From: Navarone Feekery <13634519+navarone-feekery@users.noreply.github.com> Date: Tue, 28 May 2024 18:50:23 +0200 Subject: [PATCH 46/59] [Search] Fix integration id overlap for connectors (#184353) ## Summary Connectors registered as custom integrations use the value in `connector.serviceType` as an id. However, there are some connectors that share a service type. This causes an error when running Kibana due to the id clash. This PR changes the id into a concatenation of `serviceType` and `name` to ensure all ids are unique. Errors before change (these no longer recur after the changes): ```log [2024-05-28T12:06:10.514+00:00][ERROR][plugins.customIntegrations] Integration with id=confluence already exists. [2024-05-28T12:06:10.516+00:00][ERROR][plugins.customIntegrations] Integration with id=jira already exists. [2024-05-28T12:06:10.517+00:00][ERROR][plugins.customIntegrations] Integration with id=jira already exists. [2024-05-28T12:06:10.518+00:00][ERROR][plugins.customIntegrations] Integration with id=salesforce already exists ``` --- test/api_integration/apis/custom_integration/integrations.ts | 2 +- x-pack/plugins/enterprise_search/server/integrations.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/api_integration/apis/custom_integration/integrations.ts b/test/api_integration/apis/custom_integration/integrations.ts index 13ea5a8dbe68a..a82810ea37be6 100644 --- a/test/api_integration/apis/custom_integration/integrations.ts +++ b/test/api_integration/apis/custom_integration/integrations.ts @@ -22,7 +22,7 @@ export default function ({ getService }: FtrProviderContext) { expect(resp.body).to.be.an('array'); - expect(resp.body.length).to.be(51); + expect(resp.body.length).to.be(55); // Test for sample data card expect(resp.body.findIndex((c: { id: string }) => c.id === 'sample_data_all')).to.be.above( diff --git a/x-pack/plugins/enterprise_search/server/integrations.ts b/x-pack/plugins/enterprise_search/server/integrations.ts index 82320ccd852f4..a486b8788ae31 100644 --- a/x-pack/plugins/enterprise_search/server/integrations.ts +++ b/x-pack/plugins/enterprise_search/server/integrations.ts @@ -97,7 +97,7 @@ export const registerEnterpriseSearchIntegrations = ( type: 'svg', }, ], - id: connector.serviceType, + id: `${connector.serviceType}-${connector.name}`, isBeta: connector.isBeta, shipper: 'enterprise_search', title: connector.name, From 8149486489a55b2dc22cd6f7ebf3dde617ae44a9 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 28 May 2024 19:04:11 +0200 Subject: [PATCH 47/59] Add FTR smoke tests for http version negotiation (#184006) ## Summary Add some FTR smoke test for http version negotiation. Note: version negotiation is already extensively covered with integration tests, the purpose here is to have something more end to end. --- .../plugins/core_http/server/plugin.ts | 56 +++++++++ .../core_plugins/http_versioned.ts | 115 ++++++++++++++++++ .../test_suites/core_plugins/index.ts | 1 + 3 files changed, 172 insertions(+) create mode 100644 test/plugin_functional/test_suites/core_plugins/http_versioned.ts diff --git a/test/plugin_functional/plugins/core_http/server/plugin.ts b/test/plugin_functional/plugins/core_http/server/plugin.ts index 0f1d8915825bc..534d55a97bdfe 100644 --- a/test/plugin_functional/plugins/core_http/server/plugin.ts +++ b/test/plugin_functional/plugins/core_http/server/plugin.ts @@ -31,6 +31,62 @@ export class CoreHttpPlugin implements Plugin { return res.ok({ body: req.headers }); } ); + + router.versioned + .get({ + path: '/api/core_http/public_versioned_route', + access: 'public', + enableQueryVersion: false, + }) + .addVersion({ version: '2023-10-31', validate: false }, (ctx, req, res) => { + return res.ok({ + body: { + version: '2023-10-31', + }, + }); + }); + + router.versioned + .get({ + path: '/api/core_http/internal_versioned_route', + access: 'internal', + enableQueryVersion: false, + }) + .addVersion({ version: '1', validate: false }, (ctx, req, res) => { + return res.ok({ + body: { + version: 1, + }, + }); + }) + .addVersion({ version: '2', validate: false }, (ctx, req, res) => { + return res.ok({ + body: { + version: 2, + }, + }); + }); + + router.versioned + .get({ + path: '/api/core_http/versioned_route_with_query_version', + access: 'internal', + enableQueryVersion: true, + }) + .addVersion({ version: '1', validate: false }, (ctx, req, res) => { + return res.ok({ + body: { + version: 1, + }, + }); + }) + .addVersion({ version: '2', validate: false }, (ctx, req, res) => { + return res.ok({ + body: { + version: 2, + }, + }); + }); } public start() {} diff --git a/test/plugin_functional/test_suites/core_plugins/http_versioned.ts b/test/plugin_functional/test_suites/core_plugins/http_versioned.ts new file mode 100644 index 0000000000000..1186a1f77c215 --- /dev/null +++ b/test/plugin_functional/test_suites/core_plugins/http_versioned.ts @@ -0,0 +1,115 @@ +/* + * 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 '@kbn/core-provider-plugin/types'; +import { PluginFunctionalProviderContext } from '../../services'; + +export default function ({ getService }: PluginFunctionalProviderContext) { + const supertest = getService('supertest'); + + // routes defined in the `core_http` test plugin + describe('Route version negotiation', () => { + describe('internal route', () => { + it('serves requests with the handler corresponding to the specified version', async () => { + await supertest + .get('/api/core_http/internal_versioned_route') + .set('Elastic-Api-Version', '1') + .expect(200) + .then((response) => { + expect(response.body).to.eql({ + version: 1, + }); + }); + await supertest + .get('/api/core_http/internal_versioned_route') + .set('Elastic-Api-Version', '2') + .expect(200) + .then((response) => { + expect(response.body).to.eql({ + version: 2, + }); + }); + }); + + it('returns the version in response headers', async () => { + await supertest + .get('/api/core_http/internal_versioned_route') + .set('Elastic-Api-Version', '1') + .expect(200) + .then((response) => { + expect(response.headers['elastic-api-version']).to.eql('1'); + }); + await supertest + .get('/api/core_http/internal_versioned_route') + .set('Elastic-Api-Version', '2') + .expect(200) + .then((response) => { + expect(response.headers['elastic-api-version']).to.eql('2'); + }); + }); + + it('requires the version header to be set', async () => { + await supertest + .get('/api/core_http/internal_versioned_route') + .unset('Elastic-Api-Version') + .expect(400) + .then((response) => { + expect(response.body.message).to.match(/Please specify.+version/); + }); + }); + }); + + describe('public route', () => { + it('serves requests with the handler corresponding to the specified version', async () => { + await supertest + .get('/api/core_http/public_versioned_route') + .set('Elastic-Api-Version', '2023-10-31') + .expect(200) + .then((response) => { + expect(response.body).to.eql({ + version: '2023-10-31', + }); + }); + }); + + it('returns the version in response headers', async () => { + await supertest + .get('/api/core_http/public_versioned_route') + .set('Elastic-Api-Version', '2023-10-31') + .expect(200) + .then((response) => { + expect(response.headers['elastic-api-version']).to.eql('2023-10-31'); + }); + }); + }); + + describe('query version', () => { + it('does not allow passing the version in the query by default', async () => { + await supertest + .get('/api/core_http/internal_versioned_route') + .unset('Elastic-Api-Version') + .query({ apiVersion: '2' }) + .expect(400); + }); + + it('allows passing the version in the query when enabled via enableQueryVersion', async () => { + await supertest + .get('/api/core_http/versioned_route_with_query_version') + .unset('Elastic-Api-Version') + .query({ apiVersion: '2' }) + .expect(200) + .then((response) => { + expect(response.body).to.eql({ + version: '2', + }); + }); + }); + }); + }); +} diff --git a/test/plugin_functional/test_suites/core_plugins/index.ts b/test/plugin_functional/test_suites/core_plugins/index.ts index 1423a882fc5a2..07e258e34e3f1 100644 --- a/test/plugin_functional/test_suites/core_plugins/index.ts +++ b/test/plugin_functional/test_suites/core_plugins/index.ts @@ -24,6 +24,7 @@ export default function ({ loadTestFile }: PluginFunctionalProviderContext) { loadTestFile(require.resolve('./chrome_help_menu_links')); loadTestFile(require.resolve('./history_block')); loadTestFile(require.resolve('./http')); + loadTestFile(require.resolve('./http_versioned')); loadTestFile(require.resolve('./dynamic_contract_resolving')); }); } From ae733a74abb901fcab56d78ecec4cf261b806435 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 28 May 2024 19:13:41 +0200 Subject: [PATCH 48/59] [SLOs] Prevent unnecessary log error (#184349) ## Summary Prevent unnecessary log error while parsing empty filter !! --- .../slo/server/services/find_slo_groups.ts | 10 +++------- .../slo/server/services/get_slos_overview.ts | 9 ++------- .../slo/server/services/summary_search_client.ts | 10 ++-------- .../services/transform_generators/common.ts | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/x-pack/plugins/observability_solution/slo/server/services/find_slo_groups.ts b/x-pack/plugins/observability_solution/slo/server/services/find_slo_groups.ts index d3e4670f025bb..c3fadc6f60ba0 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/find_slo_groups.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/find_slo_groups.ts @@ -17,7 +17,7 @@ import { DEFAULT_SLO_GROUPS_PAGE_SIZE } from '../../common/constants'; import { IllegalArgumentError } from '../errors'; import { typedSearch } from '../utils/queries'; import { EsSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary'; -import { getElasticsearchQueryOrThrow } from './transform_generators'; +import { getElasticsearchQueryOrThrow, parseStringFilters } from './transform_generators'; const DEFAULT_PAGE = 1; const MAX_PER_PAGE = 5000; @@ -50,12 +50,8 @@ export class FindSLOGroups { const groupsFilter = [params.groupsFilter ?? []].flat(); const kqlQuery = params.kqlQuery ?? ''; const filters = params.filters ?? ''; - let parsedFilters: any = {}; - try { - parsedFilters = JSON.parse(filters); - } catch (e) { - this.logger.error(`Failed to parse filters: ${e.message}`); - } + const parsedFilters = parseStringFilters(filters, this.logger); + const settings = await getSloSettings(this.soClient); const { indices } = await getListOfSummaryIndices(this.esClient, settings); diff --git a/x-pack/plugins/observability_solution/slo/server/services/get_slos_overview.ts b/x-pack/plugins/observability_solution/slo/server/services/get_slos_overview.ts index f3c25f46bf71a..2c1ff46f57ef5 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/get_slos_overview.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/get_slos_overview.ts @@ -17,7 +17,7 @@ import { AlertsClient } from '@kbn/rule-registry-plugin/server'; import moment from 'moment'; import { observabilityAlertFeatureIds } from '@kbn/observability-plugin/common'; import { typedSearch } from '../utils/queries'; -import { getElasticsearchQueryOrThrow } from './transform_generators'; +import { getElasticsearchQueryOrThrow, parseStringFilters } from './transform_generators'; import { getListOfSummaryIndices, getSloSettings } from './slo_settings'; export class GetSLOsOverview { @@ -36,12 +36,7 @@ export class GetSLOsOverview { const kqlQuery = params.kqlQuery ?? ''; const filters = params.filters ?? ''; - let parsedFilters: any = {}; - try { - parsedFilters = JSON.parse(filters); - } catch (e) { - this.logger.error(`Failed to parse filters: ${e.message}`); - } + const parsedFilters = parseStringFilters(filters, this.logger); const response = await typedSearch(this.esClient, { index: indices, diff --git a/x-pack/plugins/observability_solution/slo/server/services/summary_search_client.ts b/x-pack/plugins/observability_solution/slo/server/services/summary_search_client.ts index 19bccfe72c537..d99679fe01ac0 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/summary_search_client.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/summary_search_client.ts @@ -17,7 +17,7 @@ import { toHighPrecision } from '../utils/number'; import { createEsParams, typedSearch } from '../utils/queries'; import { getListOfSummaryIndices, getSloSettings } from './slo_settings'; import { EsSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary'; -import { getElasticsearchQueryOrThrow } from './transform_generators'; +import { getElasticsearchQueryOrThrow, parseStringFilters } from './transform_generators'; import { fromRemoteSummaryDocumentToSloDefinition } from './unsafe_federated/remote_summary_doc_to_slo'; import { getFlattenedGroupings } from './utils'; @@ -65,13 +65,7 @@ export class DefaultSummarySearchClient implements SummarySearchClient { pagination: Pagination, hideStale?: boolean ): Promise> { - let parsedFilters: any = {}; - - try { - parsedFilters = JSON.parse(filters); - } catch (e) { - this.logger.error(`Failed to parse filters: ${e.message}`); - } + const parsedFilters = parseStringFilters(filters, this.logger); const settings = await getSloSettings(this.soClient); const { indices } = await getListOfSummaryIndices(this.esClient, settings); const esParams = createEsParams({ diff --git a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts index fd0a65d9c3887..283bc375a0db0 100644 --- a/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts +++ b/x-pack/plugins/observability_solution/slo/server/services/transform_generators/common.ts @@ -6,7 +6,8 @@ */ import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; -import { kqlQuerySchema, QuerySchema } from '@kbn/slo-schema'; +import { QuerySchema, kqlQuerySchema } from '@kbn/slo-schema'; +import { Logger } from '@kbn/logging'; import { SLODefinition } from '../../domain/models'; import { getDelayInSecondsFromSLO } from '../../domain/services/get_delay_in_seconds_from_slo'; import { InvalidTransformError } from '../../errors'; @@ -30,6 +31,19 @@ export function getElasticsearchQueryOrThrow(kuery: QuerySchema = '') { } } +export function parseStringFilters(filters: string, logger: Logger) { + if (!filters) { + return {}; + } + try { + return JSON.parse(filters); + } catch (e) { + logger.error(`Failed to parse filters: ${e.message}`); + } + + return {}; +} + export function parseIndex(index: string): string | string[] { if (index.indexOf(',') === -1) { return index; From 38d5784008886ecc953ece84efec5cefb68ca6fd Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Tue, 28 May 2024 12:47:07 -0500 Subject: [PATCH 49/59] fix(console): parsing --euiFixedHeadersOffset when it is calc() (#184362) ## Summary Fix console bug where parsing the `--euiFixedHeadersOffset` results in setting the open console height to `NaNpx`. ### 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 --- .../containers/embeddable/_variables.scss | 2 +- .../embeddable/console_resize_button.test.ts | 53 +++++++++++++++++++ .../embeddable/console_resize_button.tsx | 21 ++++++-- 3 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 src/plugins/console/public/application/containers/embeddable/console_resize_button.test.ts diff --git a/src/plugins/console/public/application/containers/embeddable/_variables.scss b/src/plugins/console/public/application/containers/embeddable/_variables.scss index 9623db93b4ea7..d2d3854e32191 100644 --- a/src/plugins/console/public/application/containers/embeddable/_variables.scss +++ b/src/plugins/console/public/application/containers/embeddable/_variables.scss @@ -2,4 +2,4 @@ $embeddableConsoleBackground: lightOrDarkTheme($euiColorDarkestShade, $euiColorI $embeddableConsoleText: lighten(makeHighContrastColor($euiColorLightestShade, $embeddableConsoleBackground), 20%); $embeddableConsoleBorderColor: transparentize($euiColorGhost, .8); $embeddableConsoleInitialHeight: $euiSizeXXL; -$embeddableConsoleMaxHeight: calc(100vh - var(--euiFixedHeadersOffset, 0)); +$embeddableConsoleMaxHeight: calc(100vh - var(--euiFixedHeadersOffset, 0) - $euiSize); diff --git a/src/plugins/console/public/application/containers/embeddable/console_resize_button.test.ts b/src/plugins/console/public/application/containers/embeddable/console_resize_button.test.ts new file mode 100644 index 0000000000000..d42db5b1c7eb6 --- /dev/null +++ b/src/plugins/console/public/application/containers/embeddable/console_resize_button.test.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { EuiThemeComputed } from '@elastic/eui'; +import { getCurrentConsoleMaxSize } from './console_resize_button'; + +describe('Console Resizing Tests', () => { + describe('getCurrentConsoleMaxSize', () => { + let windowSpy: jest.SpyInstance; + let mockHeaderOffset: string; + const mockBodyStyle = { + getPropertyValue: jest.fn().mockImplementation(() => mockHeaderOffset), + }; + let mockTheme: EuiThemeComputed<{}>; + beforeEach(() => { + mockTheme = { + size: { + base: '16px', + }, + } as unknown as EuiThemeComputed<{}>; + mockHeaderOffset = '48px'; + windowSpy = jest.spyOn(window, 'window', 'get'); + windowSpy.mockImplementation(() => ({ + getComputedStyle: jest.fn().mockReturnValue(mockBodyStyle), + innerHeight: 1000, + })); + }); + afterEach(() => { + windowSpy.mockRestore(); + }); + + it('computes max size with base size offset', () => { + expect(getCurrentConsoleMaxSize(mockTheme)).toBe(936); + }); + it('can handle failing to parse base size', () => { + mockTheme = { + size: { + base: undefined, + }, + } as unknown as EuiThemeComputed<{}>; + expect(getCurrentConsoleMaxSize(mockTheme)).toBe(936); + }); + it('can handle failing to parse header offset', () => { + mockHeaderOffset = 'calc(32px + 48px)'; + expect(getCurrentConsoleMaxSize(mockTheme)).toBe(984); + }); + }); +}); diff --git a/src/plugins/console/public/application/containers/embeddable/console_resize_button.tsx b/src/plugins/console/public/application/containers/embeddable/console_resize_button.tsx index 0b29214594440..dcfac089437bb 100644 --- a/src/plugins/console/public/application/containers/embeddable/console_resize_button.tsx +++ b/src/plugins/console/public/application/containers/embeddable/console_resize_button.tsx @@ -26,11 +26,26 @@ export interface EmbeddedConsoleResizeButtonProps { setConsoleHeight: React.Dispatch>; } +const parseOrDefaultValue = (value: string, defaultValue: number): number => { + try { + const result = parseInt(value, 10); + if (!isNaN(result) && result >= 0) { + return result; + } + } catch { + // ignore bad values + } + return defaultValue; +}; + export function getCurrentConsoleMaxSize(euiTheme: EuiThemeComputed<{}>) { - const euiBaseSize = parseInt(euiTheme.size.base, 10); + const euiBaseSize = parseOrDefaultValue(euiTheme.size.base, 16); const winHeight = window.innerHeight; - const bodyStyle = getComputedStyle(document.body); - const headerOffset = parseInt(bodyStyle.getPropertyValue('--euiFixedHeadersOffset') ?? '0px', 10); + const bodyStyle = window.getComputedStyle(document.body); + const headerOffset = parseOrDefaultValue( + bodyStyle.getPropertyValue('--euiFixedHeadersOffset') ?? '0px', + 0 + ); // We leave a buffer of baseSize to allow room for the user to hover on the top border for resizing return Math.max(winHeight - headerOffset - euiBaseSize, CONSOLE_MIN_HEIGHT); From c7eda998a96f56b4afb4ef31735aee7d97472ef0 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Tue, 28 May 2024 13:57:50 -0400 Subject: [PATCH 50/59] [ResponseOps] add license check for connector execution (#181504) resolves https://github.com/elastic/response-ops-team/issues/198 ## Summary Ensures that connector execution doesn't occur for connectors that need a higher license than what is currently being used. ## To Verify The basic idea is to create a > basic license connector in a trial license, and then downgrade the license to basic and see if that connector can still be run. Any connector besides server log and index can be used - those are the only connectors licensed for basic. Start ES as follows to run with a trial license: yarn es snapshot --license trial Create a connector, and also create an alerting rule which uses the connector, and is arranged to always be active and alert on check intervals (not on status change). Ensure the connector runs when the alert is active, and that you can run it in "test" mode from the connector page. Then go here, to change your license to basic: http://localhost:5601/app/management/stack/license_management You should now see the following sorts of messages logged by Kibana: ``` [ERROR][plugins.taskManager] Task actions:.email "a0df7b77-b6e5-461e-9699-178d6878f235" failed: Error: Action type .email is disabled because your basic license does not support it. Please upgrade your license. [WARN ][plugins.alerting.index-threshold] Rule "c6780109-15b8-4816-ba96-266e4ddc482d" skipped scheduling action "gmail" because it is disabled [ERROR][plugins.actions] Action 'gmail' failed: Action type .email is disabled because your basic license does not support it. Please upgrade your license. ``` When attenpting to "test" the connector, you should see the following result: ``` Test failed to run The following error was found: Forbidden ``` (we'll attempt to improve the message later as a follow-up) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/action_type_registry.test.ts | 49 +++++++++++++++++++ .../actions/server/action_type_registry.ts | 6 +++ .../server/lib/action_executor.test.ts | 21 ++++++++ x-pack/plugins/actions/server/plugin.test.ts | 6 +++ .../tests/actions/connector_types/email.ts | 19 +++++++ 5 files changed, 101 insertions(+) diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index dfc42e42b0b34..76f9ae838de45 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -610,6 +610,26 @@ describe('actionTypeRegistry', () => { beforeEach(() => { actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); actionTypeRegistry.register(fooActionType); + // @ts-expect-error accessing private property for testing + actionTypeRegistry.inMemoryConnectors.push({ + ...fooActionType, + id: 'foo-preconfig', + name: 'Foo-preconfig', + actionTypeId: 'foo', + isPreconfigured: true, + config: {}, + secrets: {}, + }); + // @ts-expect-error accessing private property for testing + actionTypeRegistry.inMemoryConnectors.push({ + ...fooActionType, + id: 'foo-system', + name: 'Foo-system', + actionTypeId: 'foo', + isSystemAction: true, + config: {}, + secrets: {}, + }); }); test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { @@ -627,6 +647,35 @@ describe('actionTypeRegistry', () => { notifyUsage: true, }); }); + + test('should return true for enabled type', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + const result = actionTypeRegistry.isActionExecutable('123', 'foo'); + expect(result).toEqual(true); + }); + + test('should return false when license invalid', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ + isValid: false, + reason: 'invalid', + }); + const result = actionTypeRegistry.isActionExecutable('123', 'foo'); + expect(result).toEqual(false); + }); + + test('should return true for disabled type, but preconfigured connector', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false); + const result = actionTypeRegistry.isActionExecutable('foo-preconfig', 'foo'); + expect(result).toEqual(true); + }); + + test('should return true for disabled type, but system connector', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + mockedActionsConfig.isActionTypeEnabled.mockReturnValue(false); + const result = actionTypeRegistry.isActionExecutable('foo-system', 'foo'); + expect(result).toEqual(true); + }); }); describe('getAllTypes()', () => { diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 894ef5ed9d868..615b0b43e9d6c 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -88,6 +88,12 @@ export class ActionTypeRegistry { actionTypeId: string, options: { notifyUsage: boolean } = { notifyUsage: false } ) { + const validLicense = this.licenseState.isLicenseValidForActionType( + this.get(actionTypeId), + options + ).isValid; + if (validLicense === false) return false; + const actionTypeEnabled = this.isActionTypeEnabled(actionTypeId, options); const inMemoryConnector = this.inMemoryConnectors.find( (connector) => connector.id === actionId diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 58e8b3a100ab0..63fe8a871b0c4 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -1288,6 +1288,27 @@ describe('Action Executor', () => { }); } }); + + test(`${label} throws error when license is not valid for the type of action`, async () => { + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce( + connectorSavedObject + ); + connectorTypeRegistry.get.mockReturnValueOnce(connectorType); + connectorTypeRegistry.isActionExecutable.mockReturnValue(false); + connectorTypeRegistry.ensureActionTypeEnabled.mockImplementation(() => { + throw new Error('nope'); + }); + + if (executeUnsecure) { + await expect(() => actionExecutor.executeUnsecured(executeUnsecuredParams)).rejects.toThrow( + 'nope' + ); + expect(connectorType.executor).not.toHaveBeenCalled(); + } else { + await expect(() => actionExecutor.execute(executeParams)).rejects.toThrow('nope'); + expect(connectorType.executor).not.toHaveBeenCalled(); + } + }); } }); diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 9737afdb095c0..bcf4ce23c8c66 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -737,6 +737,12 @@ describe('Actions Plugin', () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + const platinumLicense = licensingMock.createLicense({ + license: { status: 'active', type: 'platinum' }, + }); + // @ts-ignore + plugin.licenseState.updateInformation(platinumLicense); + pluginSetup.registerType({ id: '.cases', name: 'Cases', diff --git a/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/email.ts b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/email.ts index 7b901810d8e70..983f5a1d14f30 100644 --- a/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/email.ts +++ b/x-pack/test/alerting_api_integration/basic/tests/actions/connector_types/email.ts @@ -35,5 +35,24 @@ export default function emailTest({ getService }: FtrProviderContext) { 'Action type .email is disabled because your basic license does not support it. Please upgrade your license.', }); }); + + it('should not execute a pre-configured email connector because of license', async () => { + await supertest + .post('/api/actions/connector/my-test-email/_execute') + .set('kbn-xsrf', 'foo') + .send({ + params: { + to: 'someone@example.com', + subject: 'testing', + message: 'still testing', + }, + }) + .expect(403, { + error: 'Forbidden', + message: + 'Action type .email is disabled because your basic license does not support it. Please upgrade your license.', + statusCode: 403, + }); + }); }); } From 08901cdb0c426e987bb89ebf64cc0a19905e4ca9 Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Tue, 28 May 2024 20:14:55 +0200 Subject: [PATCH 51/59] Allow the rule types to throw user errors (#184213) Fixes: #180421 This PR allows ruleType executors to throw errors with source info (FRAMEWORK or USER). Please follow the instructions in the issue to validate the PR. --- .../task_runner/rule_type_runner.test.ts | 45 ++++++++++++++++++- .../server/task_runner/rule_type_runner.ts | 4 +- .../server/task_runner/task_runner.test.ts | 36 ++++++++++++++- 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/alerting/server/task_runner/rule_type_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_type_runner.test.ts index e5779dd6eacad..888ed3b864831 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_type_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_type_runner.test.ts @@ -23,7 +23,13 @@ import { publicRuleResultServiceMock } from '../monitoring/rule_result_service.m import { wrappedScopedClusterClientMock } from '../lib/wrap_scoped_cluster_client.mock'; import { wrappedSearchSourceClientMock } from '../lib/wrap_search_source_client.mock'; import { NormalizedRuleType } from '../rule_type_registry'; -import { ConcreteTaskInstance, TaskStatus } from '@kbn/task-manager-plugin/server'; +import { + ConcreteTaskInstance, + createTaskRunError, + TaskErrorSource, + TaskStatus, +} from '@kbn/task-manager-plugin/server'; +import { getErrorSource } from '@kbn/task-manager-plugin/server/task_running'; const alertingEventLogger = alertingEventLoggerMock.create(); const alertsClient = alertsClientMock.create(); @@ -622,6 +628,43 @@ describe('RuleTypeRunner', () => { expect(alertsClient.logAlerts).not.toHaveBeenCalled(); }); + test('should return user error when rule type executor throws a user error', async () => { + const err = createTaskRunError(new Error('fail'), TaskErrorSource.USER); + ruleType.executor.mockImplementationOnce(() => { + throw err; + }); + + const { error } = await ruleTypeRunner.run({ + context: { + alertingEventLogger, + flappingSettings: DEFAULT_FLAPPING_SETTINGS, + queryDelaySec: 0, + ruleId: RULE_ID, + ruleLogPrefix: `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}'`, + ruleRunMetricsStore, + spaceId: 'default', + }, + alertsClient, + executionId: 'abc', + executorServices: { + dataViews, + ruleMonitoringService: publicRuleMonitoringService, + ruleResultService: publicRuleResultService, + savedObjectsClient, + uiSettingsClient, + wrappedScopedClusterClient, + wrappedSearchSourceClient, + }, + rule: mockedRule, + ruleType, + startedAt: new Date(DATE_1970), + state: mockTaskInstance().state, + validatedParams: mockedRuleParams, + }); + + expect(getErrorSource(error!)).toEqual(TaskErrorSource.USER); + }); + test('should handle reaching alert limit when rule type executor succeeds', async () => { alertsClient.hasReachedAlertLimit.mockReturnValueOnce(true); ruleType.executor.mockResolvedValueOnce({ state: { foo: 'bar' } }); diff --git a/x-pack/plugins/alerting/server/task_runner/rule_type_runner.ts b/x-pack/plugins/alerting/server/task_runner/rule_type_runner.ts index da29b87714683..cfac117c8f0fd 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_type_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_type_runner.ts @@ -14,6 +14,7 @@ import { TaskErrorSource, } from '@kbn/task-manager-plugin/server'; import { some } from 'lodash'; +import { getErrorSource } from '@kbn/task-manager-plugin/server/task_running'; import { IAlertsClient } from '../alerts_client/types'; import { MaintenanceWindow } from '../application/maintenance_window/types'; import { ErrorWithReason } from '../lib'; @@ -287,10 +288,11 @@ export class RuleTypeRunner< `rule execution failure: ${context.ruleLogPrefix}`, err.message ); + return { error: createTaskRunError( new ErrorWithReason(RuleExecutionStatusErrorReasons.Execute, err), - TaskErrorSource.FRAMEWORK + getErrorSource(err) || TaskErrorSource.FRAMEWORK ), stackTrace: { message: err, stackTrace: err.stack }, }; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 79f810b35de6d..d87dbf17f2d89 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -20,6 +20,7 @@ import { } from '../types'; import { ConcreteTaskInstance, + createTaskRunError, isUnrecoverableError, TaskErrorSource, } from '@kbn/task-manager-plugin/server'; @@ -3389,7 +3390,7 @@ describe('Task Runner', () => { ); }); - test('returns user error if all the errors are user error', async () => { + test('returns user error if all the errors reported by getLastRunResults are user error', async () => { rulesClient.getAlertFromRaw.mockReturnValue(mockedRuleTypeSavedObject as Rule); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(mockedRawRuleSO); @@ -3415,6 +3416,39 @@ describe('Task Runner', () => { expect(getErrorSource(runnerResult.taskRunError as Error)).toBe(TaskErrorSource.USER); }); + test('returns a taskRunError as user error when ruleType.executor throws a user error', async () => { + const taskRunError = createTaskRunError(new Error(GENERIC_ERROR_MESSAGE), TaskErrorSource.USER); + + ruleType.executor.mockImplementation( + async ({ + services: executorServices, + }: RuleExecutorOptions< + RuleTypeParams, + RuleTypeState, + AlertInstanceState, + AlertInstanceContext, + string, + RuleAlertData + >) => { + throw taskRunError; + } + ); + + const taskRunner = new TaskRunner({ + ruleType, + internalSavedObjectsRepository, + taskInstance: mockedTaskInstance, + context: taskRunnerFactoryInitializerParams, + inMemoryMetrics, + }); + rulesClient.getAlertFromRaw.mockReturnValue(mockedRuleTypeSavedObject as Rule); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(mockedRawRuleSO); + + const runnerResult = await taskRunner.run(); + + expect(getErrorSource(runnerResult.taskRunError as Error)).toBe(TaskErrorSource.USER); + }); + function testAlertingEventLogCalls({ ruleContext = alertingEventLoggerInitializer, ruleTypeDef = ruleType, From 5794235d0d562af70032b1c64af622f8a32a9515 Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Tue, 28 May 2024 20:16:18 +0200 Subject: [PATCH 52/59] Set validation errors in subaction framework as user errors (#184317) Resolves: #183867 This PR sets schema validation errors in subaction framework as user error. ## To validate: - Create a connector with subaction. (e.g. IBM Resilient) - Modify schema validation code to get it throw an error ``` action.schema.validate({...subActionParams, foo:'bar'}); ``` - Create a rule with that connector. And let it run, there should be a user error on /`api/task_manager/metrics` API --- .../sub_action_framework/executor.test.ts | 22 +++++++++++++++++++ .../server/sub_action_framework/executor.ts | 6 ++++- .../tests/actions/connector_types/opsgenie.ts | 12 +++++----- .../actions/connector_types/resilient.ts | 8 +++---- .../tests/actions/connector_types/tines.ts | 10 ++++----- .../actions/sub_action_framework/index.ts | 2 +- 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/actions/server/sub_action_framework/executor.test.ts b/x-pack/plugins/actions/server/sub_action_framework/executor.test.ts index 1280c70d0cd84..35b1fa43c6ce3 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/executor.test.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/executor.test.ts @@ -20,6 +20,7 @@ import { TestExecutor, } from './mocks'; import { IService, ServiceParams } from './types'; +import { getErrorSource, TaskErrorSource } from '@kbn/task-manager-plugin/server/task_running'; describe('Executor', () => { const actionId = 'test-action-id'; @@ -174,6 +175,27 @@ describe('Executor', () => { ); }); + it('marks schema validation errors as user error', async () => { + const executor = createExecutor(TestExecutor); + + try { + await executor({ + actionId, + params: { subAction: 'echo', subActionParams: { id: 'test-id', foo: 'bar' } }, + config, + secrets, + services, + configurationUtilities: mockedActionsConfig, + logger, + }); + } catch (e) { + expect(getErrorSource(e)).toBe(TaskErrorSource.USER); + expect(e.message).toBe( + 'Request validation failed (Error: [foo]: definition for this key is missing)' + ); + } + }); + it('throws if the method does not exists', async () => { const executor = createExecutor(TestExecutor); diff --git a/x-pack/plugins/actions/server/sub_action_framework/executor.ts b/x-pack/plugins/actions/server/sub_action_framework/executor.ts index 2a68a060e15c4..d9f2f693c175d 100644 --- a/x-pack/plugins/actions/server/sub_action_framework/executor.ts +++ b/x-pack/plugins/actions/server/sub_action_framework/executor.ts @@ -6,6 +6,7 @@ */ import { Logger } from '@kbn/core/server'; +import { createTaskRunError, TaskErrorSource } from '@kbn/task-manager-plugin/server'; import { ActionsConfigurationUtilities } from '../actions_config'; import { ExecutorType } from '../types'; import { ExecutorParams, SubActionConnectorType } from './types'; @@ -80,7 +81,10 @@ export const buildExecutor = < try { action.schema.validate(subActionParams); } catch (reqValidationError) { - throw new Error(`Request validation failed (${reqValidationError})`); + throw createTaskRunError( + new Error(`Request validation failed (${reqValidationError})`), + TaskErrorSource.USER + ); } } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts index 35d6d66f935fd..48386480b00da 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/opsgenie.ts @@ -210,7 +210,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [message]: expected value of type [string] but got [undefined])', }); @@ -230,7 +230,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [alias]: expected value of type [string] but got [undefined])', }); @@ -263,7 +263,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [responders.0]: types that failed validation:\n- [responders.0.0.type]: types that failed validation:\n - [responders.0.type.0]: expected value to equal [team]\n - [responders.0.type.1]: expected value to equal [user]\n - [responders.0.type.2]: expected value to equal [escalation]\n - [responders.0.type.3]: expected value to equal [schedule]\n- [responders.0.1.id]: expected value of type [string] but got [undefined]\n- [responders.0.2.username]: expected value of type [string] but got [undefined])', }); @@ -293,7 +293,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [responders.0]: types that failed validation:\n- [responders.0.0.name]: expected value of type [string] but got [undefined]\n- [responders.0.1.id]: expected value of type [string] but got [undefined]\n- [responders.0.2.username]: expected value of type [string] but got [undefined])', }); @@ -396,7 +396,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [visibleTo.0]: types that failed validation:\n- [visibleTo.0.0.type]: expected value to equal [team]\n- [visibleTo.0.1.id]: expected value of type [string] but got [undefined]\n- [visibleTo.0.2.id]: expected value of type [string] but got [undefined]\n- [visibleTo.0.3.username]: expected value of type [string] but got [undefined])', }); @@ -461,7 +461,7 @@ export default function opsgenieTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [details.bananas]: expected value of type [string] but got [number])', }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts index a61696934d77b..c9b4e7ecafa3f 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/resilient.ts @@ -273,7 +273,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { message: 'an error occurred while running the action', service_message: 'Request validation failed (Error: [incident.name]: expected value of type [string] but got [undefined])', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, }); }); }); @@ -299,7 +299,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [incident.name]: expected value of type [string] but got [undefined])', }); @@ -328,7 +328,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [comments]: types that failed validation:\n- [comments.0.0.commentId]: expected value of type [string] but got [undefined]\n- [comments.1]: expected value to equal [null])', }); @@ -357,7 +357,7 @@ export default function resilientTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [comments]: types that failed validation:\n- [comments.0.0.comment]: expected value of type [string] but got [undefined]\n- [comments.1]: expected value to equal [null])', }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts index fb86cc94776dc..53dd3aaaf026a 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/connector_types/tines.ts @@ -229,7 +229,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [storyId]: expected value of type [number] but got [undefined])', }); @@ -273,7 +273,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [webhook.storyId]: expected value of type [number] but got [undefined])', }); @@ -297,7 +297,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [webhook.name]: expected value of type [string] but got [undefined])', }); @@ -321,7 +321,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [webhook.path]: expected value of type [string] but got [undefined])', }); @@ -345,7 +345,7 @@ export default function tinesTest({ getService }: FtrProviderContext) { status: 'error', retry: true, message: 'an error occurred while running the action', - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [webhook.secret]: expected value of type [string] but got [undefined])', }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts index 85e3b8405257f..c8fb3e4b6ce2d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/sub_action_framework/index.ts @@ -170,7 +170,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { message: 'an error occurred while running the action', retry: true, connector_id: res.body.id, - errorSource: TaskErrorSource.FRAMEWORK, + errorSource: TaskErrorSource.USER, service_message: 'Request validation failed (Error: [id]: expected value of type [string] but got [undefined])', }); From 5a74376da0acd05388ddde264ab3409dd939051f Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 28 May 2024 20:07:06 +0100 Subject: [PATCH 53/59] skip failing ES promotion suites (#184319) --- .../apps/integrations_feature_flag/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts index 5464cf07f02e3..ea165d6f39195 100644 --- a/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/integrations_feature_flag/index.ts @@ -16,7 +16,8 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService, getPageObjects } = providerContext; - describe('endpoint', function () { + // FLAKY: https://github.com/elastic/kibana/issues/184319 + describe.skip('endpoint', function () { const ingestManager = getService('ingestManager'); const log = getService('log'); const endpointTestResources = getService('endpointTestResources'); From 8ecee1f7e153bc801a1342f89db7623a7d8ce697 Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Tue, 28 May 2024 21:57:06 +0200 Subject: [PATCH 54/59] [Fleet] moved root privilege callout with data stream info to create/edit package policy page (#184190) ## Summary Address feedback in https://github.com/elastic/kibana/pull/184119#issuecomment-2127689576 Relates https://github.com/elastic/ingest-dev/issues/3357 Moved root privileges callout with data streams from package policy submit modal to the create/edit package policy page itself, so it is more persistent than a modal window. To verify: - Go to System integration / Add integration - Verify that the require root callout shows the data streams that require root image - Go to System integration / Existing policies / Edit integration - Verify that the require root callout is visible with data stream info image - For package where all data streams require root, the callout is unchanged. image image - The require root callout is removed from the submit confirmation modal. image ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [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 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/confirm_deploy_modal.tsx | 11 -- .../single_page_layout/confirm_modal.tsx | 103 ------------------ .../single_page_layout/hooks/form.tsx | 15 +-- .../single_page_layout/index.test.tsx | 96 ++-------------- .../single_page_layout/index.tsx | 27 +---- .../single_page_layout/root_callout.tsx | 49 +++++++++ .../create_package_policy_page/types.ts | 1 - .../edit_package_policy_page/index.tsx | 14 +++ 8 files changed, 75 insertions(+), 241 deletions(-) delete mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/confirm_modal.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/root_callout.tsx diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/confirm_deploy_modal.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/confirm_deploy_modal.tsx index ce6af848ee5f3..1ede87ba34c9b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/confirm_deploy_modal.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/confirm_deploy_modal.tsx @@ -11,7 +11,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import type { AgentPolicy } from '../../../types'; -import { UnprivilegedAgentsCallout } from '../create_package_policy_page/single_page_layout/confirm_modal'; export const ConfirmDeployAgentPolicyModal: React.FunctionComponent<{ onConfirm: () => void; @@ -76,16 +75,6 @@ export const ConfirmDeployAgentPolicyModal: React.FunctionComponent<{ />
- {showUnprivilegedAgentsCallout && ( - <> - - - - )} void; - onCancel: () => void; - agentPolicyName: string; - unprivilegedAgentsCount: number; - dataStreams: Array<{ name: string; title: string }>; -} - -export const UnprivilegedConfirmModal: React.FC = ({ - onConfirm, - onCancel, - agentPolicyName, - unprivilegedAgentsCount, - dataStreams, -}: UnprivilegedConfirmModalProps) => { - return ( - - } - onCancel={onCancel} - onConfirm={onConfirm} - cancelButtonText={ - - } - confirmButtonText={ - - } - buttonColor="warning" - > - - - ); -}; - -export const UnprivilegedAgentsCallout: React.FC<{ - agentPolicyName: string; - unprivilegedAgentsCount: number; - dataStreams: Array<{ name: string; title: string }>; -}> = ({ agentPolicyName, unprivilegedAgentsCount, dataStreams }) => { - return ( - - {dataStreams.length === 0 ? ( - - ) : ( - <> - -
    - {dataStreams.map((item) => ( -
  • {item.title}
  • - ))} -
- - )} -
- ); -}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 54199ef90a06f..f6fe2d3582ccc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -31,10 +31,7 @@ import { PACKAGE_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT, } from '../../../../../../../../common'; -import { - getMaxPackageName, - isRootPrivilegesRequired, -} from '../../../../../../../../common/services'; +import { getMaxPackageName } from '../../../../../../../../common/services'; import { useConfirmForceInstall } from '../../../../../../integrations/hooks'; import { validatePackagePolicy, validationHasErrors } from '../../services'; import type { PackagePolicyValidationResults } from '../../services'; @@ -270,16 +267,6 @@ export function useOnSubmit({ setFormState('CONFIRM'); return; } - if ( - packageInfo && - isRootPrivilegesRequired(packageInfo) && - (agentPolicy?.unprivileged_agents ?? 0) > 0 && - formState !== 'CONFIRM' && - formState !== 'CONFIRM_UNPRIVILEGED' - ) { - setFormState('CONFIRM_UNPRIVILEGED'); - return; - } let createdPolicy = overrideCreatedAgentPolicy; if (selectedPolicyTab === SelectedPolicyTab.NEW && !overrideCreatedAgentPolicy) { try { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx index ab87cbc8492be..d94f386d6aa36 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.test.tsx @@ -316,7 +316,7 @@ describe('When on the package policy create page', () => { (sendCreatePackagePolicy as jest.MockedFunction).mockClear(); }); - test('should show unprivileged warning modal on submit if conditions match', async () => { + test('should show root privileges callout on create page', async () => { (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( getMockPackageInfo({ requiresRoot: true }) ); @@ -324,70 +324,15 @@ describe('When on the package policy create page', () => { render('agent-policy-1'); }); - let saveBtn: HTMLElement; - await waitFor(() => { - saveBtn = renderResult.getByText(/Save and continue/).closest('button')!; - expect(saveBtn).not.toBeDisabled(); - }); - - await act(async () => { - fireEvent.click(saveBtn); - }); - - await waitFor(() => { - expect( - renderResult.getByText('Unprivileged agents enrolled to the selected policy') - ).toBeInTheDocument(); - expect(renderResult.getByTestId('unprivilegedAgentsCallout').textContent).toContain( - 'This integration requires Elastic Agents to have root privileges. There is 1 agent running in an unprivileged mode using Agent policy 1. To ensure that all data required by the integration can be collected, re-enroll the agent using an account with root privileges.' + expect(renderResult.getByText('Requires root privileges')).toBeInTheDocument(); + expect(renderResult.getByTestId('rootPrivilegesCallout').textContent).toContain( + 'Elastic Agent needs to be run with root/administrator privileges for this integration.' ); }); - - await waitFor(() => { - saveBtn = renderResult.getByText(/Add integration/).closest('button')!; - }); - - await act(async () => { - fireEvent.click(saveBtn); - }); - - expect(sendCreatePackagePolicy as jest.MockedFunction).toHaveBeenCalledTimes(1); }); - test('should show unprivileged warning and agents modal on submit if conditions match', async () => { - (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( - getMockPackageInfo({ requiresRoot: true }) - ); - (sendGetAgentStatus as jest.MockedFunction).mockResolvedValueOnce({ - data: { results: { total: 1 } }, - }); - await act(async () => { - render('agent-policy-1'); - }); - - await act(async () => { - fireEvent.click(renderResult.getByText(/Save and continue/).closest('button')!); - }); - - await waitFor(() => { - expect(renderResult.getByText('This action will update 1 agent')).toBeInTheDocument(); - expect( - renderResult.getByText('Unprivileged agents enrolled to the selected policy') - ).toBeInTheDocument(); - expect(renderResult.getByTestId('unprivilegedAgentsCallout').textContent).toContain( - 'This integration requires Elastic Agents to have root privileges. There is 1 agent running in an unprivileged mode using Agent policy 1. To ensure that all data required by the integration can be collected, re-enroll the agent using an account with root privileges.' - ); - }); - - await act(async () => { - fireEvent.click(renderResult.getAllByText(/Save and deploy changes/)[1].closest('button')!); - }); - - expect(sendCreatePackagePolicy as jest.MockedFunction).toHaveBeenCalled(); - }); - - test('should show unprivileged warning modal with data streams on submit if conditions match', async () => { + test('should show root privileges callout with data streams on create page', async () => { (useGetPackageInfoByKeyQuery as jest.Mock).mockReturnValue( getMockPackageInfo({ dataStreamRequiresRoot: true }) ); @@ -395,38 +340,15 @@ describe('When on the package policy create page', () => { render('agent-policy-1'); }); - let saveBtn: HTMLElement; - await waitFor(() => { - saveBtn = renderResult.getByText(/Save and continue/).closest('button')!; - expect(saveBtn).not.toBeDisabled(); - }); - - await act(async () => { - fireEvent.click(saveBtn); - }); - - await waitFor(() => { - expect( - renderResult.getByText('Unprivileged agents enrolled to the selected policy') - ).toBeInTheDocument(); - expect(renderResult.getByTestId('unprivilegedAgentsCallout').textContent).toContain( - 'This integration has the following data streams that require Elastic Agents to have root privileges. There is 1 agent running in an unprivileged mode using Agent policy 1. To ensure that all data required by the integration can be collected, re-enroll the agent using an account with root privileges.' + expect(renderResult.getByText('Requires root privileges')).toBeInTheDocument(); + expect(renderResult.getByTestId('rootPrivilegesCallout').textContent).toContain( + 'This integration has the following data streams that require Elastic Agents to have root privileges. To ensure that all data required by the integration can be collected, enroll agents using an account with root privileges.' ); - expect(renderResult.getByTestId('unprivilegedAgentsCallout').textContent).toContain( + expect(renderResult.getByTestId('rootPrivilegesCallout').textContent).toContain( 'Nginx access logs' ); }); - - await waitFor(() => { - saveBtn = renderResult.getByText(/Add integration/).closest('button')!; - }); - - await act(async () => { - fireEvent.click(saveBtn); - }); - - expect(sendCreatePackagePolicy as jest.MockedFunction).toHaveBeenCalledTimes(1); }); test('should create package policy on submit when query param agent policy id is set', async () => { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index 1781e79c999bc..4bc608e4d9059 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -79,7 +79,7 @@ import { useDevToolsRequest, useOnSubmit, useSetupTechnology } from './hooks'; import { PostInstallCloudFormationModal } from './components/cloud_security_posture/post_install_cloud_formation_modal'; import { PostInstallGoogleCloudShellModal } from './components/cloud_security_posture/post_install_google_cloud_shell_modal'; import { PostInstallAzureArmTemplateModal } from './components/cloud_security_posture/post_install_azure_arm_template_modal'; -import { UnprivilegedConfirmModal } from './confirm_modal'; +import { RootPrivilegesCallout } from './root_callout'; const StepsWithLessPadding = styled(EuiSteps)` .euiStep__content { @@ -463,15 +463,6 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ dataStreams={rootPrivilegedDataStreams} /> )} - {formState === 'CONFIRM_UNPRIVILEGED' && agentPolicy ? ( - setFormState('VALID')} - onConfirm={onSubmit} - unprivilegedAgentsCount={agentPolicy?.unprivileged_agents ?? 0} - agentPolicyName={agentPolicy?.name ?? ''} - dataStreams={rootPrivilegedDataStreams} - /> - ) : null} {formState === 'SUBMITTED_NO_AGENTS' && agentPolicy && packageInfo && @@ -515,21 +506,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ )} {packageInfo && isRootPrivilegesRequired(packageInfo) ? ( <> - - } - > - - + ) : null} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/root_callout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/root_callout.tsx new file mode 100644 index 0000000000000..3a4c01528f4de --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/root_callout.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiCallOut } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; + +interface Props { + dataStreams: Array<{ name: string; title: string }>; +} + +export const RootPrivilegesCallout: React.FC = ({ dataStreams }) => { + return ( + + } + data-test-subj="rootPrivilegesCallout" + > + {dataStreams.length === 0 ? ( + + ) : ( + <> + +
    + {dataStreams.map((item) => ( +
  • {item.title}
  • + ))} +
+ + )} +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts index 0d533597a60eb..1a203c8bf2e60 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/types.ts @@ -19,7 +19,6 @@ export type PackagePolicyFormState = | 'VALID' | 'INVALID' | 'CONFIRM' - | 'CONFIRM_UNPRIVILEGED' | 'LOADING' | 'SUBMITTED' | 'SUBMITTED_NO_AGENTS' diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx index 751e0cf233b97..c7238da1eac1e 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/edit_package_policy_page/index.tsx @@ -55,6 +55,13 @@ import type { PackagePolicyEditExtensionComponentProps } from '../../../types'; import { ExperimentalFeaturesService, pkgKeyFromPackageInfo } from '../../../services'; import { generateUpdatePackagePolicyDevToolsRequest } from '../services'; +import { + getRootPrivilegedDataStreams, + isRootPrivilegesRequired, +} from '../../../../../../common/services'; + +import { RootPrivilegesCallout } from '../create_package_policy_page/single_page_layout/root_callout'; + import { UpgradeStatusCallout } from './components'; import { usePackagePolicyWithRelatedData, useHistoryBlock } from './hooks'; import { getNewSecrets } from './utils'; @@ -387,6 +394,7 @@ export const EditPackagePolicyForm = memo<{ ), [packagePolicyId, packagePolicy] ); + const rootPrivilegedDataStreams = packageInfo ? getRootPrivilegedDataStreams(packageInfo) : []; return ( @@ -426,6 +434,12 @@ export const EditPackagePolicyForm = memo<{ onCancel={() => setFormState('VALID')} /> )} + {packageInfo && isRootPrivilegesRequired(packageInfo) ? ( + <> + + + + ) : null} {isUpgrade && upgradeDryRunData && ( <> From 25812971d0bb95601334edd0776db4c4e262e864 Mon Sep 17 00:00:00 2001 From: amyjtechwriter <61687663+amyjtechwriter@users.noreply.github.com> Date: Wed, 29 May 2024 02:13:13 +0100 Subject: [PATCH 55/59] [DOCS] Adds the whats new page for 8.14 (#183556) --- docs/user/images/alerts.gif | Bin 0 -> 130943 bytes docs/user/images/case-action.gif | Bin 0 -> 327155 bytes docs/user/images/control-settings.png | Bin 0 -> 246876 bytes docs/user/images/controls-apply-button.png | Bin 0 -> 383900 bytes docs/user/images/custom-descriptions.png | Bin 0 -> 392945 bytes docs/user/images/esql-data-viz.png | Bin 0 -> 195489 bytes docs/user/images/esql-field-stats.png | Bin 0 -> 132533 bytes docs/user/images/esql-viz-saved-search.png | Bin 0 -> 138865 bytes docs/user/images/gauge-chart.png | Bin 0 -> 173532 bytes docs/user/images/jira-connector.png | Bin 0 -> 74671 bytes .../images/query-history-in-dashboard.png | Bin 0 -> 348091 bytes .../user/images/query-history-in-discover.png | Bin 0 -> 217506 bytes docs/user/images/region-map.png | Bin 0 -> 503824 bytes docs/user/whats-new.asciidoc | 221 +++++------------- 14 files changed, 62 insertions(+), 159 deletions(-) create mode 100644 docs/user/images/alerts.gif create mode 100644 docs/user/images/case-action.gif create mode 100644 docs/user/images/control-settings.png create mode 100644 docs/user/images/controls-apply-button.png create mode 100644 docs/user/images/custom-descriptions.png create mode 100644 docs/user/images/esql-data-viz.png create mode 100644 docs/user/images/esql-field-stats.png create mode 100644 docs/user/images/esql-viz-saved-search.png create mode 100644 docs/user/images/gauge-chart.png create mode 100644 docs/user/images/jira-connector.png create mode 100644 docs/user/images/query-history-in-dashboard.png create mode 100644 docs/user/images/query-history-in-discover.png create mode 100644 docs/user/images/region-map.png diff --git a/docs/user/images/alerts.gif b/docs/user/images/alerts.gif new file mode 100644 index 0000000000000000000000000000000000000000..dbc8c1b9b8621065cff2dda7aa5068a3e056589c GIT binary patch literal 130943 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%0D%U-LB~`iz8U{!s7%7DQD8R!IDzT6 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 TZNE{oR5B*7d5#8y0*C((oy6AU literal 0 HcmV?d00001 diff --git a/docs/user/images/case-action.gif b/docs/user/images/case-action.gif new file mode 100644 index 0000000000000000000000000000000000000000..32999b698473b4a03d8431ada05b3cdae0b86ce4 GIT binary patch literal 327155 zcmWhzcRbXO8~@zk&fZ(LjKtY9&fc7TM2=+7jF31EcV_mAoRUq-2&wLDLPU~L$`+9= z%J2Joy`Jaw{PjGq=bvZ1pXY68Y@nj*#sLDrCjbBeKtMi7HYG{1rwiar;7gjy=qKe3 zE@rRHvFK;wlT&sK3BpDORK~N|jH%K0w-cOniY%6BR&1Y+ZF+N)IR(8>}nS?!tB!$+nNF!PtWGI zfzq|#pQb+pgl%zy@Q*+CtP@*kHb1v~S}JU74wyFDh{z|O|1!nD2dFy>3yUAk?poAV(}w?<0t_g;x_&HvGb;X6 zT~#Zqpv)N3ZFA>!2)a{^jIMcetm0jL1udKferj*PZ|p zui874@3h3FtkZ^h@A()5Yn_HqzihVp%1v$pYMU;%+<|hF^%_Nhr|;K|SeC~=LN65)7{JW3lS(A zyXCX&*qI&dSa`K)-?C&|O;v;c%a};}XhK|^ms8UKpy2E{(Es%}jnf7LoJTsq59qX0 zzFP_Dxb4(FJH=(DvN_@TV|HtnWNsMXQM*Qa<6+R;8H?Hg0Qr{mc)j-ZPDT5x=Dt2z z{qz9;97Mt^WrGgIs6pkrYRRIu(BT64s4&%n99i|i;GcNAA`t|v1esjTuv>5B;>*>4q-D+;7j{4QsdK8WY6|_q_qFqWumfAWrZ|GkG z;36!1tw)Ceqod00O#o+ecx;u(caSiPURhHJFmXjzpJr1@2BdZmenID>@6z+@A0vJ znZ@j5Uhuz)g|JF+BRFX~qU~ZFtn_XQFXIA8!6sy#9UmMpr%?ObFjYD+3~6~;{3Fn$ zMS^Ho?|Rv$s6x5!xi$1TXqJPN>>B|ZSa9fVe}>lOthd22s2^? zsAp01VgY^GFdgxp&@`&Cj?gqZr&DI(jF@98E5C2k!Xq36Mqz#U84+c9BP`~(_6>rw z?L&%Js)DKUhALK;bcWNqOUm&kMits|qZLT%&h& zae4P~9m>xRBgmj7IE2$@gV^HwUoHvAZR>w))x`%7NUM`*2ak2N;i*G7P6LR*==Z6C+u^}-W|ov+f@y9YS}>1?zg?TZ){Jn=<}omY2XCY8%!(!ur+QZ@x+s3< zx#>oHsxN%Ydg6;?m1at3lH`7^w58o!cU{9d*v?yEf7DDlz(x~T5mubGF}%_?ZSRnAG~;k@tZn8@ z<@)ig$NJCdFW%LS$8-L#c8=$R2d|$jgnbS;S&Uq7Jo)Oab-8r%4GWV#T}tE#JpE1( zYdZaruKcUxo0P`m$dy!^f7n&5VW#RzTu|)UQXYQ%Y`uI6^JlZBy6MkW!>eC^el`zE zpKrH*4m{uKSZ_N2)qV2oe3t~1x!CLD2)g(^B=-Dbe^hz*;$Xs1=I`OOL(t!&S+D1R zj~DLk{yka3%ltcCDGd5|wo(23-=FPQyZ_Gj24yZU4nGH7{yhz{)c<#JvU_<6&=LWd z5edw_hlQIF!5l_N-U(7XV+;{0NWf7$?Im!I5-F93$n?>BI04!UxS>%GbID$!xLF14 zC#i?MD=SH22};9h)VuozK@m=>p#Le5#5J>rpSxedc!5-^%}hTrtAQn!HH&P8Z%45v1Z4 zUkVG15oRbB9MoKE;*~(U@YNgX$^ZGC_0k3_!WnO<+|^8(tXC=G=m4EE*2#$Sa2IhL zH27o}oa4C5JJ!#A2s2ElnmbnQ6Olzhg&Z++*$y#CGtJ56S<^A9XxWyhh#1{O@sqP zGBbdpA0d5|;P0qXI9MMt3}g;3RpGg|DR7w-l=J=3b13QB_2Mh=JmWGU>MFXeW~kVn z+v9&vJ#j0=YWH4XnWV4)utl)Iegw>`m#dg2jUkBU7CI#i2q@i)=bi>V`CIQrV_a;? z)4&At$O6;7q>AT;+FlW)z{-R6uyV2qa*WN|G6DiATBN)<=C-PzzE~yhQIbR$06@cj ze`L(aeI!3E4zSzP=iHr=2#=Bc=ixMV|8Zb3TOwBGdzZn~^{K+Li#sl>e~|8V`H!9b z1;w@mKE3P7YfKEUcVil!p_GVt5zG6O(TDnrlgUg{;rH8juJg%pK;`|zz|C#=jB@<+zWI6Kw-a|3z^F4?V?ipA86*s+h@X6 z#9`O1svYJY9EUSvDmR@?HRKI4-!$ZI2JpWhS06cHV4#3#qDe3V9U|{}zKpDV>f~38 zwU%@N&t!hIp@nS@#UESLyGdRTWt&M*%Z~B@&g?>28`lwHr4|o0-00S$}ne z5St@E;FGlY&rEq`p00DKLtQRAH%3k5r^T?+jLVgUdHc_bf()kKZ2Tft!=l0NR59W@ zsvC@W-)&oF#jHNazCen`($h<-H{w0~DuPrLOS;UZ!5F%cNlCGhPRev|wuDPm<}GXm z+&@+KiU=7dao7Qmj78GzFOjb9e`g3FYO|c8GSyh)8FIht^4KOpIB*qIjLno-8!^ma zR}&q>DHfbM0X48?hPg`jKuY%Fuc}y8^Q&wXaM6J*R=$Huy?Yu&WpOa+A_jAWJ0E8} z*R8b*jzZH2o@g>$I?m}c^+tqD0m)Bz|2t@593u6pf?nd{I|EvYqr}u40w0~It=C-4 zt5x|jI9F2CRYk@>3Z4{|if0NKdQlZZydtoS9Mi{m@JMg=!dNS&87md(OD6oKbEiJD zdO@LI%f5*^x6Z%0OcWKC`b82YMN{dkM8*oD63y063Z>AyQB!`mC~_Vxof#~?)%W{A z#7(x-eQcPlPzihDaPaoiwf%py9q;N4qlRoxRySWZ9W}jf8gh13{9yWCTM|97&8ByM z?nA6LZNn9qZ``@LdgK)B$&FvZA6|bw$vkQKP}YfM=w7m#y*I1q@^Cj`ac(T|He4=t zC!(6TB78bLViWv}C6BmvaO(ta@pnHjE@nkE@&taX;`h1w-%YNd$ItS7w)e_O#^(pSE)4g7z{*ehVVXZBP9r5%9K&zpgIunkMz5L1W|Tkg z`uemXs##ezA-qxZGW&WtdGprS9yod96o~Yk0ucLP3iL$eK9a(9 zFN(eo9MTMxnV@t$hG8Zs(`|9Tl;cjlu*^pBF?emg(?rKvydFF*QaqM95?`oHNZP<3 zoZ$TpJ#U>lKfWTaUChx>HyY>g1Jl zeKv}PGwt08%oYj4?1BFxA)%;bg$a73x1kIGikV1`6QEipg2xQ8X!L^%hv?yrM0g}2 zf-_x~ka)oG%B}}( zYA>q8zpjU)Ps%IrWqoA1mERGjAcCRssW8&3xOi4UsgQJKs{-0rAr!5bAxdH5@Pxyu zRIu*J)XJ^W%_j=1g)+7ka%xWybLmYj(A9~jt9$y&sagti*!tT+hj)yq#BUt6sZ-TG zxj5F5?NWHfc~bSYVb96<|9B|v|GZ3{8nmjhuzUIJ);4*%6TV<_90ipYP4cT_p ztY)tIsNJZq#B9@~sk|(+6T4Iu&h3A?gU7a!Kl{@bB)?MNLdas@@E0^^- zRJ~WLPg84r-Ts*7K{k!69$Wk1fbDaSApUctMWwsvA0UkA*^>y7QLH}p)N8D?(9{bW zJOtjeszwdGRs>8}Dg&hl;BmnrEnA&0^1-RE)yyv^D7Q!$39OeswlQi68imC`>wy4f zSbQJ+4P%?;CrWB$Gq#E*9}41Mst!Tu{C3fSLZ zq-WEr81!I-685fvhDVYl$=7#SO0$SOU2G$^nlz!ZpFJ7Z+=WxD(8;$E>ln`dr}`wY zaMU{md=RwoO@WB#Dh5UMQ!fso8?ThZgog|ccwVNuM@@94&Fj#@pM6vBJ899aFzqPC zbVPL9>n1uXVmcp~X~m^GhHo2BaXzfO>>P?78mj#=^d=?osWSGN<8XyTnTG0cQTK3e zcV=1o@Z-|qC%hw-v?Fg%hD*$djW$CrB3(D5wamMGUq~5Qh@sik-lx6_@7fNplX@_L zd#`1iFVh+20*iek^_n1tZlWd9=e3P*kD_0j8L5v=UyL^2?3yVZo1+CSZ;U!%#uhJJ z7DmTpirN;t$JTjUH~r)MJjS=o8a8>ymT5mYrnPOxI1I67^zdd3UX1izd;omO76^1w z%=_1)8XnYx}?CoM`*7|&phM0XLNc6L|3&z<;;Gb6>Q zdqjVHms@#u@!|U7$7W%(v_xXxCGAv0ubs63#c39}7X`B*aw%dNC<7coWoib#YH`4z zgtA3>m^sy~F^18j*5)>{P0dqHU2|o+`@*d**It;b{m(N43%V&GLOu1E+K{Q~z_vR5 zSa1{y9Q0{_Sgrq+L(wn`LW^Oy!!xq_LepR(mlr>dT0qC`^s^y64r!=X5 zV3pUYLP+p1z|FUy+Q-f|0AnOVR~^3hG<*+69)bHax2t#ObVrEv+zNZy zg$UVI^4eOeX2^q0)zkPN0*`jO5M@npuLb9s=gZUe0|pR@rE0@ADD5YjI3nQl!gF=w%h^S6Bpeq^&$DJ-oSNc1W!L8s~fp060i&jIG21a`0XbRim@ z|Du9;e>@+Zpf_J^ou<@Bz!->iX8-JYo*49YkKC0iyq*W9TpXRI`}QUm>vd%cRH7d9 z>|=ZG$M?CFT@5pOkE1?XJPXRm5p90c*??_WkjC>)^h;}yU2ZJX{R;ZD{ef?1ApO1H zUr4YESka#NQTlUTua1S`gzlAxIAv#@$Gc)44rl-DFWzduDg9NaF?r#<@@_r+1@CIx zg8pLcFa0Ls!`M6$F5*U z%~G9H4QC?oY+Lv-YQF4J@=xmw%f1&tGbtx4NFc zeEz2uhkoUM-u>`_rB^6PLSATrse&P&_-Q`{Q79uBE&-aRcmJ9!873Lde_CF) zyi>CchA2@op5JOFT4hldq3_#rddyCI*CjBCT2!JZf9R)kDcY}0O$wK*3F^-Xz?lpY zdE$+nko{4MkvzF$^e}+Cg}9BN7a79J%{)SL(epCqlIa^Dp1w{|T%)Nx%8Wr1;vXx` zS$HYkP!)p9CQ`Q@QoFxE3}`c%DdxYV^#6br-POXhlE42jQ0I>72wa)}yz5GOpcTvN zh|kT-VRWN>?JgBx?A6>rT0u;q>*FR~M!^eqOqwA%c=(N6auHrxIg8>EKK<)T9z(5J zz@>04K?F{@v;CJPo%&6bZCxkX(|mv|ukdg9RB(Bm0MTtn5i1fP>7*}hLFkPZsR$R*+Ry(;=V%}f;0onBdg1hmPd?IdeMP&;evu1f5}euC|E zw|;gjSV_Pp;m#W<|9{|{?O)M%2HLA7sg$%Bt7d^MAxSX(63SG9nZ_ zBhM2IC_X#hegACebGAn~q5jS6-(wfKWxI*Rh;L=ex1z3be|C;NANysHr6)J-nn0tZ z;t-|_KE@>TM9dD5d*uDQ@z>fSs#U6MqIr`!(P6j9z)#tHg6V`}U3d0a>qSiUT@$Ji9Vk-)5fwO3*R^&FJh5oCk#} zuB|{~DzV`~y?&8E@WA<<-bQ?{A3m*zJyfYIGzjNEjyWy|KJk&CF5rS`M!^!&mq=={QWdF# zfqJo%r`$q?Pxf|HC+~#(?ER+bRQs3x@`4*gAL;HpdGB(q^IL3O?9Ma;r}v+O=UQ%O zM<=erM^vLRm%;*ul?6@Ut6AQ#X)FDgKc0-~GbXUaU?@brwW)70COCdyx}>}@tIZgT zN)*f+f`~=xuvAS5NahJpJDm2gznn;tP5e$9Ag;?hgTQls;aSA=r|{j?A#_cYrU?Y= ziCncxG4*g~DXY=rNU}(^sdQ&+G=jj3f8`xXu=nsD`3Tu`59g;` z^MnEAy6iNsl^?`4C8SiW#lx!k+x%yzhFX-&Syk_!G_}_BGh0Bj3DzFMEN4dhDsnmO z*@Ajt0y5Ww3Ff#WvaWDuY?*vF_X%`W+?;A;vWyYMIl9DQ=R>CN$x7r-7rpMvMRsc6 zFA&Om2D@Kuir#;jT%bX8qa5nw=q84a^nv8dQp~)SV39U=8`Wy8 z+24v1P7g}4815p+mmdX8v-)(=B^Y5&|I>m zQ30*Pb4sFOBUJM$qDcIha0lfWc3sfHknZ6-ixPXK!NBh)l4`o z3Jjnb7{#K=L{WwBpmwshOfU*sXC(BN-aXM+yhJKEYzP<|tu9CffN<@jGF8mJgTlnMd03G+=81TUmUD=+ z0Ey6(;E=Wz;N!|TY<=D22go|0Az(jYj>hydfkHk@Pj#;P6W{xdI*wvFQ8qIO#rE+# z1}-x?R(o9b17xU;d4pOkDpU0$oRz-Sbr?KF+8Tx1`3{Nk%<$ZGQtrRDkok~0vVqk#xX&XZtc-Sy{{9QKZFBh?Asd76 zyye>MYq~sNA`S`C>Bg#vYnd8X%C`9aUJPbt{t8{-d!^O+Hv!t){&K#%q2gpM0%E{{7+oy_g-*j4@7KJ=3@ z@sAX{IrZX7#DL}41T6GSc&2KBr1~8Suy&Vz2Qo z>)ubt%V=&-kvoMS9GTc6&!17WQQqmle6V{(?*82T)G-$F`7Nhc45Z1@y|klxH;5r- zwU_$gxaHD9kygysCBp%;U+ghe$6tQx7w2D3T=zO3K0v7aTwFPEFz_P6Gq_TbHaRj=zfq zV6qCd=a36u+=4j1-zS0NK9--ckwuuGfzjbbVVg{}2bqewjozhVr0^RQhBON7OFg2T zlyDSaums^tf38H*=JbGGvWq8(*8CTuN$`HlRc~G1$QG?4nMrFb&ueYT6^>VAO@mV$ z9w`6k3pRZk-?Xo7fRyAFfS)~y*AB}@L=gph<%~CE)Nhs=JD2unCrVuO-sR${*yqq; z)o-n>{;jM3JO1sFAdkHW@!=BGo`qqvf_E#WfB%4xt1d0W)3Iiy>Jq~rw+!j6g~SXg zx$zntN*Ju^4y;Woc~+K7|0Hk+D|(o9`c(2eICpK%8<2);f}n=oRC;&A%H6rS*fj>X zI=Q!R8@8Vn+a(URh3IES8AdDc+#5Cgxj8VmJh(-b!WzjIuaKVNXoyYc3F#Y5Y~YUa z7)nkoiQeZXq#Ip6i!w@O)R9Uj2-SenVv18rHF^Id2Sm#1Neus{?tiFI{QhQ_!=|P& zfaMMb(_3R1nxAV347b-oq<%oeP)PVhk`kh{!8>6@wLjmM+;Yw~-wU&d1Om)JKo*o| z2~uMgFS?P$O#+E!B^2?>0$B+{09F}-tZ2Xs{z;qaeSU1xLCxLo73EQgF+1cd|IRREfmlkdhY-M{vMd!I@5}m(AQ&< zyTqYh{!Z1CDghb&lrgrI=OEK@BXqbi|2=CI78N*NmW(%lA1nQ0^fPQItaNZ^f$RQg z@$s2OoiWd@h-Ha!kwd-Fp$6B_+|lDaqknE6PW^dYQ}*1s$rl>oaSjVVy5IsmIiFa}5<3+(7H6!7I5d!g5ORi!hpH-1uY zd^e1T>JCBlbaJB-_rfEdJ1fymWwMBuEP9HS&4xY|(W~LruIY>C_DUZ1xuB+jM6Ka4{1I=OqfPjMUq z5)BjrrC%P^ej*4{Q-Yl9TzPJOSuX!Qnkk$>@bE?CG0yMI_PGXFhK|xBP9eeK6x2iJ z2-MB?n+YD1^u`^ONC5!3hxbFn_i^LxbfX{D|y&@@92uaA{hD^lug*Psdt6V>$flJ86eVb8MP9;MA;u_g$> zxQ5cNCh=Mgt|L6VQy!fno-mWN=ojhoWf7+pZ}LH3vg%5$Y@bcVa4+Gdq`h|G5j0&X z5}-vxAlPrGR~qZb=BcreizEor2z&7$nc8T9D;p}vOG+NXsW2|tQ40g-xoTmgUO=dC zDN(DlWMh$V91iDT8Re?LGhv%NtmD1l?V=sYt;Fn`0ps8MpS&`&K>FZm86hB5AUYVw zGmERHpk37vtISzC%y%lhPB^|3A2b1yM18!AiKm0)c_JWaEQNMG`Nk~dU>_e>!vu{T zcQbk?+6>z3{J|XpktAUuMG(m%9RDo%(zcfA%%lzmK)5NeZbaOlBu)peV(J+J&DXSQ z1-RTrt^?7z--HPf)EBIVvlse5MVVcTvV`M8A&#gxHS>HA92w$3%K^xg{<+N z>q{ClD*moFPHPs7#@vY}-pUZL;3mO%hIkn$u#1bCDGN7_!@q?C5J{u&Ea$n*#Kkc; zkX=VY0tytjbc;J=C57nGG}h+)6dYCs3WKmJhUzl7tUmn%;vqffnsjP~q}w&w8WX|S z%4Da)JktRjHHrcWKraavfE{*Y;LSSlb}f0&{c&w?0|q2l!;3({Vi2Z3`e#$l+_=46 zd9kid7*KBw-Qx=u^d5}E2>WQ@>&{=6BF!h}KSYRM$@@22g>!f{Rv~gj?%d?@#?>^t zW0|Xs**9vivkxf$lelJ_}Mi7HXr~zmQ|MdHp>bJ29DOBDc-W<2?FJ-4o8+Z+3%S>zTyd5;&{4i2{*?eoMDViJ0VhdnOiImVW|J+{DE?{lCAwFDzrVL zrV@M~uD?znlONvbS8MZuC%tXCt}{G%1x z&ujbQ6~un@Y{+Nu=sbPf?_OZu+nxXilXB66py~+7m6H$7Bk@Wnp@#PY9PX*nQx!V< z-{UX8TYm3W)CbpB_vj7pg?v7-jYr45JLwrcX$C{e|M6S}=67g32yY2VH;0af7 zN3!=Gg$bvOjsTPio_D_pFhxc2iDEcFkaoX>uGORDC2Zs@gy_H~ISYFI>i+$rc#C3; zUE--KW3rMl+uaSUDkBI%O7@PtHqI0;&BzMJBumdS(Gqv=XfL$$)Kz^js|iw5+1F4{xy6c1_Wqojzep*|C4Et+-y1@DDi*s}tNrM^H%XK&Sq2Q$G3wUmerV7hx4Ue zO3Y6a7R^L%&gbwx%ivWkpTkmw=O@u1y0~hI=#k6mO)$!uA~%dNgRR+|f-|?fhslMk z?;#3RvYe1qz@KS6X@Qnp8ZskL zf|sA}p?2tyLGo*i&NND}kRA=LDQrdB*WYc$u$8MIic_MSjuTa0I`gcWZq(5?1y>pp z1U^ZBqhXh7;!mfe5^jvM zq#*~>>*SakMz+y-{ckB=eMx<64t*?zFfr@z8Okk@Ed$)-Pq2!6PvQk<8R(inlGx{q z@QFNPLLy+%CKUm2s)bYWgu0PY-}mI$?#rh?^5OBtJXzP0C~w^yKu!ZN?_fd+-t8ml zNolwc6hzg~d~ntG>YR5P_w7^(=VITA;ZF=A%@8I<6@L{LD%hNAZK+Hawl634afg+x zq@0+zSJF(#)tjCHEJXeuf5nWb*jD6bCGl}&|1(BoC;sY}nzLp1tn<{^Xo}oTbkGj) zG$@sMkMFZ4`&+Vf(&cK!emWnSCASjaS+LosV(!jwzf^uE^C$ zjM$#@wr7b6eyMlGlvY86PFDv2Jzzx1R)QHOniEB+jrNgDhuLY;UNcJgM(;hH7%P_a z2^3V=JPS(6&rs^e0w{zz8}ga6UNYU~d81%u$P%SN1ccwLOeTZhgq7ZXW3D04-yaYa zEt<&G)L+z{w@QZ519`bH8i|W+d`}Fb2R_CyEvw5D5_uz^PDMeGik~r9?%;jg1(}LY zYFfaUDPY{1lM zap*DS`MdRtraW;qy}x^6i0Unl4Ct8bfdp+7{W}iX+hao>>;YBJ^YZ0!BBqE0oCw3s zv*tf7-wwl=%&}y`+trQB`-*fQA2qxf+kJXRv5$aQAW0a8=0~Q{ z_tdR6GiAv(NR~MgL1>!%Ab<&8HV11y%*&v>bsMTFbfUmopetH&EkPvfRqN%d_7w*2 z1lAEUo_1gGj&&sV2cH0y#1hFWBuZAZvmfPv9x!W&elUo<583%sxUX zg6TZ9p2m-!F0~i8xlyfh8k|%msFRs8uU(NZ<|1dkxOSWn1otWbn2O50syE@MQlrgB zBBm*M(B^C1)Zx1fBZDOlpYYONJY`dcznr$|w z509rRnA>WosJ3b_AhG zZX9WAOnV?@cO+>yWIa0xKo}E z6ZGzl#0`}j01-^1fCN1pT>>dBLHSFU1it6to+=h;E0Vl3GyozUeWNAaPz{c2!i!R5 zO;6n)xIF+}25!#wfEtiExIzz)Ee3i|tm^ak96jda`$)Rz3eCZ1gOmaHz4KSKlbptp zup^KF-QElOE-Rv;WxKX}D2{=4dI$Cit;8{O~ zAJPm^`6_ef?&ml;b4cw1rH14&C%$Z3jlA3o#fXf&Ik{``Zd1mP5aA(VZO!gz? zdc3nR0r2dKe`}pDRzWk$DMbal5}rQ252D0mQX=+38)1tZE=3)xb@`9dAL;2Nz8z-X z{z5--gLs9p*+#vhh=y?CvujrTz(~J@p6kdnQ=aSYvJb|%y6boO;4-&V`fomx4Ku94ag^xJ z);>yPLBb1WJT*8LJ#!gx?A$bHVFTP9|M^Rvu9zc+Wy9<}BA3-$K&&-h zWEz-R0evzU0S0g)J2kjVWi@C|WApR(ADuXMaL zGM)K__4-QbKqUh$hy{w}G;YR@`j1u=r*BPK@(GlKix}t-x?GB8A>p7kN4+Qf3u{6_GTvxROg-MJrrMdN)K=YoO<6oZLvF(S$QDvc{_+#@8b=TQlr}GE!N% zC{B!Rc@{>w_48=YXgS$HtHB9BR6Zz3({PGhrjM?GMvTCeVW4rJ!Ck>=hO1B>Ztl@; zA-atO#v{fxDQ%u1I*Cv5EE``ai7_y1aj)u@q95`}wcpU_YLQD!6ZtS#D#8nz)NB6blkQiWR0mt8&_n&xw1Ehmdta%qzo5UzGPr5rg{! zcv`uz4ffyeXfAX*67Zb68YG!`>lrfO_-fGBRh5}%!9ojeQC~v_ghPIPx=Zc#E4U{S zwHgY(LP6mgPW?5U+bd$r-fz|YUe8QKf>&hIOk~0OMq9z%Ct`Qk=!0{_?kCJBg?Po( z%>-C^J(wvlEiqPE^BRsSjQh8)+?B^q=^ePU9>^_z*D8Hk!aH!%C2PXVz@L0u8_B_1 z6M+TsG=uV|$JGQ#nl+#9H5pL0lKAXDaS|6OS4e*p07Vo|VU&VR9KyGalPldY852w9 zrhpBNmnP9%4w5BDL78f`3?4x#{%d@D#Hv-ek}{yTI*u6szJrEtZmb`Gka zGvD^HwF2B(5(*eBsMV(o^Pda(>DNd3ePm9BcukcxLG&=!!X}cGpg;?!!${;rOvUtg z?R586=s?tldVxwX7-9Nq31EwaQl{6LhmmC#bXg|i{qH8jhO`kFtegi9f&zh&2{#vK zi1r53OFBwuvRY*i5Ufi(>uWHC{Eu7S9_Xix)aLH4dz1rKU)6r=GOW-5R$Bro27}ZH zxVC6UT*>7Y(pekS9slOzO-}J%;qIUM4Y~}K+RBx+?RB+GF>vT9HNDg(c?Bm8AeGco zsHYRyh!918ovyd}9u(`gNHU9mFAFD#4cQ~SU7w4NXWQ-_ek?WO1bQc@N7wh`5wo6Z zsV$R{_Iv3>#Xj#YHIgKXEPb&#mi&Hm_?q^DHmCuhBy0~+UQgwOlDfa9n2itX>u`+v z|J=Z7O2tc`J_{a=S74Q*izG|X!rA&r*yhDUOSzjS+AyZLa`9gk;)cC!5&eP^qnuTsB%biS{hM>XAjX~?4D+xM$%1# zfZ%|@ZlHObEH(|N+t#I>t}Bo5DOUL$-SZ#B`t{OQT9BKg6On6Y(a#QrD-x(g`jE?2 zcA?|>bClvW=-rn<4G!$k-TwT%>iWgM8IfAOMbV9mj|KNjcC82^I;G#roQ)xNPjcaX zSso1USXrl&E4wmM{!lj-x_@Q5@$DU=Tm&0`Si?7M#BlHz`7Y+!Q6xR3j60;vGEhpJ zR|CsJ%6ZUOF`)4jnTJD8JY)m~d&_-8l)AD@3NeRIAlNwe%VfG}#ZOl*pQsI0+=xI^ zu9Dpo1bQbhyerzO%@upp1}fpS3Mm-A4cZEnCm*3`aS5`;3W5w|&CyzZgB!5Um@f#- ztZ8EDK3OXyk3%*RBrMp@=b#%kba#H}qI|QSJR>azCwx*OGgHfI2bWhIP0~Gc51f`t zdqHxypy!;OKDA>V6gRtKCg^ej(j#a{@xy%NTq>Gw;eMVb-Uk?e3DgGs- zqP!>PB$7&wz1@4;Uc%;|G!qj8y%4_TfcbY}NvP}2bL-lcNTo}@*sg=y5=|QO)fY1@ zK4&*omqYFEY-=8bdW0WnL0a$VBedh#O`z<}SHe73Wp#Dg1KRg>pUZ1lD%^SVT>nF; z{(pHM2^z}ShhCjV#)W0-*ijgpwk7GeN!&6~=1HqLY$J!Yxn-L@r(thAG=1G_&H#Nt z3TrtFvvj>_AwO?^pkT_Qh~jBO$tYS~3%AxfvX%+A=2x`X#n>1swpfu({Mu}S6m5d= zOUN>No<40(s6$~}L(WmD_YwMexKrU#D(Q$#Wz@0o2>tC4y{_mu)#g-vRN8&y`ns)` z_Soh8$oaLR`y)lC*KN1nDLU}CyB!?4h$*>VhTp6{x?_47;i??rMiX&USINWc*yEa# z6UXr#zau3nhamSeR2!D=OcP&p!50!%PCxOb!>;dbX6z%R0 zUuj>o-+p!C&wmetJ8`c*3iufw#9;K`wX(mLa%^wJ?cS46d8MGI%6>n?Z%?&H<|;+s zQ*wo=1S)r6-YebAjfl`X4T`&$_&Oq5_m#GI;Qgm3w|bQbX^~00;VFhne$}TbPs3B@ zkNp{rZ_%7+kA|mzJB;DEmp1r6y51|SsV{8b+(}5Ih9XVG&^v_QG4$RM5b3>12LS<- z&_WL#DN>~)AT2bdOBWOrP!XgER!~4dfAZ&>Zw_XzYvy>L?32CrTJO4_`#H(*k_#8P zOZ*fWK1xnyyif>BQ^>e|Le!Lxk5{-AdK`Ho8wx*;=!i^W>Gl6{H>Zi5CD5C(rBLuf z{w_~s%#Yt0C8t4}XBi#c`M<;CFXWS-k&9d9vkOnX=}t?$eg{a`e|e;kah)93q3CeE zr-Vf=y`!r*tvjCQ)Z^+|mErI73v!@yOOQ4BN$i`HkX zG5xuY?r&$gcXddENf!S^S-Dd}x$8pxCr5wRRplVUmQ8@4L9zAo(AL|bGD5@^#;aF|3XVg#tc;^oN(hE1O`F%@p~#0(eBgaE|`x(QEIfFnZ&zXWCiuoA5%a z=tw8Uk-A>O#oxlW;eq}5nNXECq5m<4sjejd2aWlUF=lWjZE&?jb>+seP?6r9t3&g2 z*cUHU-~Uztb40cTo}6lXo$~&6=#R?MRU-Z9DQ{H@cZ%K6jP$XLefE}I;CIYH9#|_^ z-Ld(%VtMgzJ9%)moN&d846*|4B|qKEQrj4@q^O0w`yAg|BOHNlqo-*zZ3h$EAB8fW{u~@uE@pCZM8R{YQ+0$pG|KZ+dTU$ z@^|A|VCx&@gpQu{>d2Qo=dI;u8@VSVBC4&@XZgBMcLbjOx_3? zF@76?ztDeGZ`#ZJel0&g{~?6NO7b4_9lNfZFx>4wC*Pzpr9RE#Z!Neusxc_)yKLrq zO|t~fv-XGZv1YQQL1BiGMH9q+B^TI9%7TZ+jL@yj_wQEuO=cg;hA=C1?hDDwKc2na z?sc_^%OKgFSXXnB$YtfM+FM1K?sl$Qxl&ZeyH2v~NcMkwqIISs*ZlF?GmRH}iI%SF zTY^+J-?HPpkGn?YE2guBZ5stG5)xHUg+s2qe!j?Ws-MYxtd?h62g3*&>Q|?atq_t} zf&{?@H^u}(B_am6#D|J|DNyREldG7zUU6UjXL)AaK3ZkI3^anY&wnn+#M`1j*HgK# zxIVnNU%&E^QqU3LiI0sa@k+C_`ZYv@d`I3$jB*4%B1PQE!x_9SMe(WB0YMvZ9<4Ul zt0LYtA7ORfRENq3=SckZAPPN07SOr*7$JH{(Ud7%w1P*CyeDm(qgDc#SN}UX&*IMh z+^=78wR^}Vg~b`~p@C^$)sW&i!9AkkarFt-ER|yttWLE1YQ!~U014)uD6_cGcb#697x6cyKeyhL%r&zSe^vOr^-LZe1!4627LsJ;DR(^@^Kqzc9Ag!T#908 z3@FLFqn(>svPICi0_%1Gww712y&7A4d z%gz6-Jk!rYMfczu6K$EOU;T#LQ~%|w zz(x7~akHe#`~<4p>qJ06)uha1n3mBT~~&=Cg7g5U+5Q zF)*@Lv)+O8?|$B38lOulaw+W zdMzp?PUmvJia8WA8QbC%QK)TK$x6i=%>d~eYUQUd_-;3AUQnJt839bbX9UE&v!|$6 zBrUZi)r2g2n|QP-%x{O{*q8s+kkmC>;hwL7%^;C5cZVY8+KDLe<6jQ_WLCn5sB-_H<<_38e0k`uV*dtH?;j8f5ZA<0r4GmXAjR*i=dKLqCdwm3jVPb8C?rH-#I ztJj+?ldQsdY`Q*M_ZHu530Q8jA8)+b!ERmkM6227<`=uwzGc0K+2s^S#Q#WDp#Lwa zO7bN?1qUO6JU}5e2|C^jAn??wt4UMMMZ2$Qbus~+H%Un;eA?Qj6B`-ni#r)-Y;u@F%Os7qc0x5L$Z}CY+T%w-rw`qHtx;%~O6!cQ%v(+(t?570U;yJ-X8`_$PBtu&_%-n3+nrW7+P^<`>XaQFT)kF)12=nY%0mu3 zwM`?7%)4IQCU5KZX22D*dE}l^-wg)n?JvDScK0R?l)X&~0r;&Ku-l(9x+_-RwFP zqh=h^h>iuhD`k%?bwZR>)0hw(0DFSd`z4QGpCpr(qh`nhubE^;i|tWYWIXk ztk(^ElQ$_5@ZXPcDfdcl3anj}790tVN(t+_ z;S-HNRwg`Z@}%!uiT>J^@b?S%TM?`5*BI>a$$w)IO$I9HDytr?MiyO4g+TvbQlS+X zwkL%Z{+OPeXgiUG)=d3hsnB)#sxU!b1Ypgf&|IqDQEQyw5S#v>Za#uCfQESzN%4j%2BWFhN=QP(vbSkXJnEssXVny@9FZpBz~lDN+hVHGb?}rV5S?zvdy42ly}nv9gr7z$Hh6hO;ZM_fRMxe zD$59C1iD$I92BsuJpx}0{p=fvBK8zBEIp?l|GLU7+1z{RN8H;4Hrh|ySpjL!pTNPW z|E$?=sb=92H;I51tkKc#`CFM3hEzwJQ92;3iX%RS?{NVxot9<+M}N@6VjhoZUKP&LkO zhprsY9|CY%Ryb!N67x52mozok0Cz*>Afe7=&HKRNb56mv#5HVGEPUtTwZ=MbM9TavR_k^IiAW@xW2t_ zeyej$7^p6|5cad+46n{=0dGdLx(=*I7rAu9?yWq2`me9a1)y&8wLKV9`qH5bC6PL5 z&8fb1t#~$l>wXvU!7xIg`Cx=v{_DXgN>}u7jLF9TaGcdkbdi<)?!W?-1QW%R=e#VP zy5Ss!%~I-tPkVpPV4jQqnw2TE51do@WG~62^ep}63(lS(i)o7Y)W9MQ8%<|3&HPyV zOEA~qQLm2?ZiG&<%-SfvNwQPx88$l1?BQ1FFA)$Pbqpno!3$jd$^XFC+6UAO8CzLB2uy3vIdn z?0oIji{5{*;1@Ukp@Pf*0QIP^?|+~-+#Vcpp2#$CeT$%8bx`LO?1@o7;G!)>!`gKi zDCG`(7;84vi&!+O9$@0RmntZq7j#x#<4Y77C|s_J7@-m$^FHl!fY9U&<0BUm5nNo~ zE`YW^gvoZ~z`e=s3Qx`K4G|v2yo&Fc{G(Y`^gPN&Rdfw{qavQhL^l2Re?JSLc=^Ym zDiGX0B?Ck{DT_-Sd3%TQ$;1rGJWXYGi(0IxQ%G$w*?i1xslKSSN4DMnewYhWtHKCb zbb|^^Uo}H5K$1z=45&mx-Xf}!O8o`6j6>N(p8^ykAhh(dJ|s2Hx=j8*5b)1zA^fi# z>V9Mn8n0ZVAGSK86_8HCSh;f7HqTo!4rDMVJW}$(gFG5vCGnzaq|bcx4cMQs7-ilQcqC+| z?fR;^KCBjg%YnX-GN<-4uijxK2*JDoX7RrQ#9}q$F~2D)J^0;yQNlM3bDlKBjoq0& z00R#%S>$x}$43WX%;VaMRdsky+wCu!&31AJL% z8iWjl%d&9q6ZIS@S2dnx;DGAU_fRyAl&-V1vTgi)4K&7<(cxB5lH$e62(vpenOWK=AKOo%au| zO75-oG-eWYmK$8El~McaR<7Swk#`fVTcoUWdDph|KHYz&j+wq6hQ6l^a^L zQhsz_ixX!&X=N)Cf;r*|YIF=RLI6GYNz8iss|js_k@Ukix3G55k6RLv^AE&uP*H3h z80)MfPL$dSTeo^%9WFhIHG`<|2$4`|lSux`PinotlN$#sF6h$C?61y&`ru>$v1?Yk zP^)ihTMy7wT}6bk^U8feZciThv!h;`$xc+N&jARTFlKWd;49fHq0D!BY`1&vl$DtQ z<#6p4(F;!!Y4eT1?-#L1g+R}g?(217V>C$GKa8RHFH)NXj)9@RcDj&Y&?3pZPsYEN z?)5!jkNg|D5kZ%ur9)i7!oR&x}`d6w)h6n zOq^3^@{AK@qUHvs?RE5n2LBiDz% zSwQhQpsas1zh{D;9Ce<(FbK5Wo6EmV7Nu^k&%6+DQ~NLZM0^}mpsYbhna!ei_;>kY zfd0)zcffUR|Mcp>D9M)w@tyNrEW9#wC|;grN1X_}@j8^oE0-T`-sQ=PNjD{K+>C%` z!+geY-CnqlCpw?`Eul54NI?b(tSK4*b4{X_p@`--xP~*zbQdKF=+th}HOi_`e$?mM zz%{+Hel3Y=gCOX<+#WJQd~jEcbJ>cn(03g1-(-Z~P`1~S^)!z8g|Vt0b@Ic7sD2g( zGyvSkz+qfwbG*FcGI0!*Az&9bt!|)YRL+iHhajU(0rG*XD?|(_ylF@5R>;51M$=YW zy#bk(w0r5END5k`Vl+{eyT;*pMDvS8ra%C|TSjPfL{OPKP^kMwZlS1t2j!FD-Z&Tt zV_YI*c(*=L$QqSa0DCj0A{I&I+mQ@uhnqROd1HMjf|0j~s7=0D3j7tKW*m*WLX2A| zyb@i5VrM#4Q#yn@utJ;`65ld1M6|)*46B&0I0oe6f_OB1$7Q#|9oZ3DwDeLI9q|K4 zjyAun?tANP=`nIZt~n54_Eet_~oGFaxrN&$eoOHt+q)A07KB&Y7UB?uem|OP`pU1hdNz!?O zZvp@k4NuTVc{SMXZ@@x^=)icIG9gqy8g>2P@+ymh1`Aq7!`;#FfQ2Gh<$ds?k=szA zR_+7944U0=lVb6IqD9M8U{5xZWjhnE+5#!Dydg0s2}OWY7`-HW;BhfKo98l;w7uQXp{Kh25giu~l6knrgfFU>l+*#eAIYt1hSY6ej2VK9Mv!IkFSc*%m zHRcxd0}=>C!)bzO*8!mFR&hUx_WBT{tBopf0|{)6YN5i3RBTEryeg>5A4IXd0ek6F;=Vz%H&hAPEQ8=_{$gRx`V;|X zG&BnkEevEmh}LTgWxC+Wg>y4|1!&k&sp5(uixo@&VCQ?^T1aN@!L64m1rXl?aCR+p zvpAp;#n~ozgI_joNU?v&UPwAA?bRdlD=7v^NS7jmU!P%Uil&Oy1OLJxwhK9D*Z}v3qyO$J4*WX^Ooo1Y#$AnadwYg=^yWX=Kr$B@_Fs?ff93}v%&*&0XDYQ zo8y#0h|u5D>8Ezhh+t$73g)tK)%h3UqA!ao*w7`2YPG2CQ;)LHnc+ooZU>^BwWOV0 zMBxW}>$U#&FD&^x(a10jfjV~wu4@0URjR@J%3tziPhPP=gh)?2(%=_7PiUY|9!q8` z8MgmhBXLW2a7&ll5jzN57AtklV*Du5k)_kntTBPiN2`JKVCRV1Rg z#o+FcWJ(blG|LFP_+*LbjD))6{Y*B#A=@8Q)%B0i_U%HxbBpomb?#2>P*+SY+n?0b zKdzlqC796;cwSiJkB@_Fkb0_PiQPZSW?T6s=YS&;O5A@&=L`n?8>&)=(xQqT&z{n? zKf{g&Xx1p{D-VByI{f)nw~#qgbx&E=hpP8q`j~$ zK3@xE+cdJ>r>zls&6N?>;Re6@oByr|Dgwq0X$SD9`iW$81-#WeNW}-EGM5KyfZ}ML zuz3U3GX`?L3aGRLzzHozwSkLCMUkwDMfqb?q!Dsqcpc0dzvK2?uJ`1*q@Ctm@$p-Lygr1H5eOh;o@7~WAt0%5riz@D;a? z8Q~VX%9J}e?$a7U*kT*SB8<|achesADh8EUFOM%^bf}%Yq3E2ug{#=YT_K|0^*@Ha zsU-iMeCIoTAUqEpT;NGq;1od}&MuHS7VR{dnf>es{3j^}+nHK4xo1_FTC{o&fNN7D z|2!S;4|xIrBH))i-Vdd|FbZFsgnMjWxg<=I^AgRK%4QHvgPeW?7PXitUaoh(X#s2YwvQ-=35s1 zmj+U7{J=g|qcg>C(T8BC%k`g%#3)_yet7ylmt?-UWjCGn+hEIgJY4E#W<}e^O)yZ{ zaH;n9kj16Q(cKmew>Gxh7$7wf8 z?!tj>9LlqTu|wv=rz)Qp1R1d)m_Zzjt#L8Fl%C`cv*6qY07?s5fH`P`&%DQbgV_7m zkvf2pNpI>RoJn7gk@uhw7E_ms1Vo&nGI@<`*|0BZOk(H^hBaV+gC?4`jeikuh8i`X zALNsy*fdj^xbS4;=m0NbP}vGq{H)+@ESwk?guenTyoQOZ$;iK|(0Elr6J3}8kO_o+ zrJLQ%MplrOq+D76^P*8SzM$(E7z1|a{5=X^FcmVN4Kt_Wh%Tq;%l{0c=o#XYmuw%* z5X&AaEZwJY+W=QMR|D96m4EctBIHw}KizbL_M<-wV~X@Ik-Fh!C49>cN~vE>6OD$8 zJpwNUEGC5o3H_pQugFt#2Rq_E2p+=C4*1=aQ8NT4y3FrdF-+3VpzI(xKsrEH99RT^ zU}f}Hf41{E-V3;x-ohn;(93#UTkoUWHT8FpsBgs&04ozl2IsCHeX^b3*lywW_kPYG zFU)}oGzy8KMxs6jt1*U)pvZeNW2sF4-Wl792?c9in~Gz+y#+@eYv$oRVx0HIk&|8g5$4+)(ykUYrP+I~z`@c6Anj~WAd&iYoXL*TQnuDX2Za_RNr zwP4ex<~xTb@r@FBO$RJ@qdpAIXSh5z{~gpTCXVCwlyKsbR*9JV{Ww;TF^hq2L-AN+ zm(!ns>j7->fo+(Z0&(uxA?ceue;L}zlIaS9Qh!*@{}_Cg627QQ zGJA)X>mKJ>1mA1h$angFN`|XHf$P;Ic~_hvxKkMKU5g!3@Nrkg7aZ}ld+Qe@Il@s` zfST8KVM9Hh3it{_L2zk=LIh=k0*aeG8OF!Gy%J1~Q!`^=j#*sX*3Y8|d?a($?o0(> z+@93j`t``Tl;2}|Hi{&mQFMd8fMVl`mwGki#?QP5NA<=@rZjzF!m=JWORu+czV}`J zGWA$4H|akEX`|H!?^f<`gZ!4YDz}M|48wv7ht?3f&yVg&`8o~Zp>+KAxq&Xl_vs}( z->n?FHQZy;thHbL=`oTk8MymyReCnPQdZ($NtBudHIbR|Lxj$^AJzPN!< z&cU+BjG=tg9|?x~BoR1^Ht~o}0}1&XV?)sE@_;ihQw?g%8I6Jyz^QDWWZnr@&GcDp z!Zx^7ppd(Fi#DVRsm-GAOgYXj{+;qG!B`FijK+J%RM7o3IH%_mHxcyUKLdw0Z~`cG z3c%QE3Oy@UUe!|3F?~)B1I`Z9&RRDSm@5C*{9w>vjj<@d=JJl|M=2E*PvnO9Ai(;M_KDrAQre0bLmOO zD?*95k45%GS@v%l)w|b?>!=4??Ja8KtsVmv52TL7M$5hoQ+6b>{H=+7KYjJfwid&= zYKHqc_vq=z-}Dk>SJYia5@eZygB*rsA0kX#Fh&;MSdaRAYgpLm86}aqE+Gi5aIo%7 zx>tEY| z`=`}h_kk&=B>=Ex)yj+Ag9wEFW*$3j%ZUqi4B!^h2Hr7n)2_xVQh=!vw`*wldUwQl zaY<`On+)Rmqu2i(6nZkiL;{3duK#zTr?mVx2;H{94}8VJw4^9d>*>RsQsA`PeJSWP z5>Ec=h#s2t2`~0^lrS57O&H|NbNH5(b5kR~cQ!{cxfiPC-C-KP#0Y@dh$7$ku3Vw8 z6a#Bda~tXw$(-<(6>jZ8j-{SAQjC)#O6$?jShWhY7!X_sQ_9j(q@B5!RaAr#L;2LJ zqT{$qI(u8xe?B>IKEz$lc1lr4I~0rFZoRkuqZ=ps)A)oWL6KnZmw+apxtx_IPRqxj zfD&*;*Obwbv#vR!bC_c)n>AaI1%J8B1Gz)_D9?PY%L$K?*22+}wzV1-Y_Mead{$-} zBORJwWd+3qyR=cv7grL7uTxP%*w@%FfG9T&;@DMv4T(de-LVeFDQ+$@sL&%} znwu#gA}>a>ll8S+;GKF6&I;B$IVWfdXQ(}#AxWahjqaO$f?%RDNn#-!Eq`-{Z}{h{ z++ALpge&Pjy{>&(nJ6QaIRv$^P`1t8VtmG{hgdiO(C*9E!H3BvpoRJGF*j{9E4_`~ zsjjXGda8d*Xt@Y;e|Z6((m$rzt{-7>rht%(k)9aSdn;;taz$bkuW1vni@H#I*^XY_ z4N6x1>w-!?MOQEcr`&RLWva6s7x4^EbNK7Z`rLLxGILt2S=o(Em(N6DH#jRcedap+ z)}-clf7T2QV+i{NVycSgfI~I+{T{a0;=6QP&W-5p)kX1&TwVi9(a;ZFTHAR%U#D%k z{qwtLLqr6ayzxq#NPw4{7S3v7;fmw>`R^4U7+FDSzd(=~a__`(?Qp(C%}@*%U|Ipc{t<+`dcakv z{GCc9U@9?kR{aXK>~Rc|JDxL=i}v#=ML}>V`)BJj5e;C75UJ1Gc zbj1_+ufSy??m=rBraF@K^LYm+%Et|%$lzdw~F)I$V z_nc({MifBh29KIJqqbS-aEMjI#-mTP3*S&`)q`pb)au1%Nhj7baob#Uz$8zLyyFF} zqiyTklqc&)LaY69?X*`%Ub$xkeyV!%;BgE@;@QyO=9wS4FT5wsdUkIm&3FmLua4b4 z+PCbEbJnAN`eZh|hb~eGNaN0CC2v48uG}|vFRYGWU}$t=9SsMcFkV4Z-VaH6`Abx5 z`kAIdo>gDw*;a~%ft1Cj@}jruhB9S}o`5o$D1%APSc%t0;I&sEXbe2|Rb4Z8L*9lc z%cPLmPk3x-^AtPrhV!LqfETHPRMv~{d8sbDGIZM(@X`>L!9vis07m56=53{6g^@kN zQ_5|GMPFiBY7GV;MD#&V`YA#M1*!zq>&|Pd0YXM(E+E+Y9fhcuLa4=KFQC%I+eAuP z(&gAKF`k@=TaCAh9%%%%iCAi^>QNfR_dX=ypd=hKKvN?usUxKv zK;I8aiS`905)h$+NE0g_osUFTGdu@9QrA!Jc{HB&K&=%kI}$R+d^-3x2ydx^K-LTI z{%VyM#_aoPRtXDLD3ZR^3xhbGP6&?3Fg4+w1?ShaRZH8vz7BXZas2jSlpcL3JR{c1 zI)Yz;xO1oKRSzw;#Y0yi1MCp%gQqdvdeD@He$k9VcJ&M` z>Vorl3ELDE%e2$b+S{1q+kGvAQgwK=26Mj}fmMd;*Ruo5@mfF1R9+(BD*+%f((Na| zuEdvXshzj+G*P$54XEcPa8doq?w(cY|4W%OV93-VIVdV=Q`PnKsBKDbX2>5iB07tj zG90rvn(#MzK9)WjV>DH2^zw2dag;d)%+64^Wxq8#U!i%Chq0$NeknRr5$9h~8G4n{9>zUuS6O}a(|1K!X$>U206%q&`h#YJ%rx5bOc4Q;@5cxMNxFbEdb7=DKgo#%3R?UuC6czoZ&{S z#E2QwQWD;Y@x_pLA|97)Xa*WB(b)-2u_LEBU`pI@bISvQgh+x-y(yb5{C3+F3iLol zWbb$o;#xDv&n`VQ2q#%Va3h*N2j-qcf+L*aoap$+uRyE|;`DnjejL3}nyiBUfdjSx z8q4d$PNbW}hZI5g?33NdeH}3i7gYDa5QXGW41tq`*J8yR2u}SQums4K6O}zID_ zx8S>w9Dl3j}zJ;-PK7=N043;W@0Sp1jin!O7%jioVDi*rOPM%o>uY97$|bw7ipJg@aX}Cb_ZV z_nB-4>2tRsg8ZS&n2vOwKTG>TGy&m84`ki2hWc`upDL-#4e( zgxr*4r>`sP{H458)HL6joWIudz>nZSv4OnIhqILrr=bYW>OkLn?yyi^B*+4U~ zLH%?C!MsTa+hm}1xx(+l{N0%*ZWAWw!sfWiTI+%c*t{CyLZ?Fwle(8TG#p0c}+f6qbMsIsfBA(8oNFUoMikK%MdsD@Qj!$YVG{? z)b&EzIztlXbgg&e0jfbhb89$AH=n`<`80(i(Jgbqt&eq!`5s>W;C=OhgSuzR{w7h8 zgyZ*N05-rnYBIW<2yJnMCIrxQ+tv?oCq%mIh`T*opkYqSLb7^TaPHuXmh_=G3r7!Y z>m92I59`bw3r=F}r1=dHa62kJz>M>d*zU%cVe`61dUEIa0r2pfp=n%tggU}Cf;!W9 zH`Cdv0q4mzlMum^B5ezq#=yFWb1FKdGIGke{cb#ZS60C*j1zH3oEQoN?l^vm$9WO3 zUQxR%m`u-7!IYdx0ub%=%gr*xwAs*v6q1lE=J3Vh)1na)ErsGm$~@ ziWrcdadIdtkrBW#1`#UN@cPT;XlBkD?2N8^dAlEO4gBz%ayCmc(EHE;jvj`s> z2@cd_zOe!q_XYwZ!8~oHmO3e(a>(oec)|kV9vOcjRxygXl=cQGgzq9U?+DeEZpo9f z1knDd3Orj79)`x_^^zUX{)!5oyJl1PATWyD15U>Rh zH5O#qp9~rTfFYcgZ4iKg9sW(J8T(XC3ZMjVVf8RtF0d~#4+h|P&=AU@?>l5a8-9qq z?e{ZBUs!|n!y?=zW6OV#WOJ3c(rtE$)m=G&`9^}|z45|&BFRKhAPE8}6dITuz;#ou zdwXS)K>(&piUUZ&`Q-`*T*MFMz|8lHO^+g;ef0(fp6=*} zgW*`1z(FDsn}pm*u6T%hPXRb#z$qtC_3JzJ)?rEApOGX;TqIZto5WZTK7oBseeGM0 zqNKeaq&XQvK>NjFD44zd0NfW@XHabYxA-CoiAWObiW~u}7*E=jDnN3dWHFNfwL+_; zhdU|V-h?6#BL9x1+|Hb5L~1}pJbAZ7ju<9)ys4AYD~B++P3l^{eODt*>0ev2c)AL$ zN(uSI-|16Cl2`Q8laH^zuNG0uWRjd7r-aDgZHoyKccON!J$!iJ#@v4w6W41lf7jR3 zigh$JLubdbd#}V*z{9=#PO{BjQcq<14Wbb-@woCZ&g;`%x~S~yQQ6N8eLS}in}L@y zM7(oUp;MF)@vO)xs<=GrLC;y?v#65fsNx?{gcY17=7GX-*EuwQL#dr@7hMFD|YA_<0f%-&XnZJ&$^LY{MfHztZN$mQTB%frxFd zis$f*#+D~T;bC_#D_6y>t*-D$WqS9$B4)vVd@SAn0hrWYa>>wJpNyIx-S2pOO;(Pc zneC?<5qBRiayMk_!zO^}lZYNa`}1G4TQzfD*YdBE%)Zsv?=}<94s)izn3|71-#Y4A z`ouN)qo8}H11e>>Un^%$XF z6k9RX)!9QE{#Q^?V(H88@rfzV2hXoVC;=H#*UVcAtJg0kcaz&*;k$PtvO6F5zuv0^ zQ1#XT|KN9xWT&=B027ty(E+G@HyIgFNIIb_R+aB*RnN=Pb#QjpyO$8P)jBcN;o-!s z{M78{r|4KbaI?cCfh=D2G_3TKOng-2%_P9RPi%4DH0g_2`Ivp|BHP8U`jfX0oV+U@ zR;nv|Gy&p1uPc_W8e3`lShWqh+BGR?cH2ZpS>no7McHrmFW*q>U5L%sQZ?~kf1|3F zc%yQilan`Sw#5I-&g-ILU2VOJrw16!SkKbC5RZ)2iV6UD3zR@*?!Kn`{ONfF}$;?TDABnsL-XBv0YwJIzigxdPOv6kFZl%k-_TI`+*sb5nR5{t(%EH2g zwn-J9$>tK0Eogo(($%_7VF#h0aMryG8B=KD!URYa4cp z{kv~*(ZlFLbtNYA`k%@olgKmbT-$8h_Hq2kJ*|D{c~u$HCW=BRN$#_^3765SAc@GI zw^(D9iQBI&jrhD@SCJ%qX~-$?{oGJr+xWS$x%)hal4AJc^XIIPdEYNBWa|`&mi~6? zN4EYDrsh^k7ZKsZ+-pH)`$Eq!|;+< zZamlS^F6ls$?(Jg)A>(Lsql@o^4hF3?xx;hYRV+jcPQ#d-vb=^EK*VahwGd&wTW10UlZT{re)rGZmPNH=zfHi(8C^UGMI{f z(NM3i(O6THkO_OsJxKHaEJ1zz11m}-FG9;+~d*Ac7 zf4=uZF76!+LePJ{ROAdWZBEKRW15<+<`~qXFl}90c&(cko7%?U_(gMrwkF1er^du( zSAO}}B<(rm8*0xy@%8 zJ4jNPt)#s>Im&syfc)*_(s+RaaQq52NaIt8CaH*#D_WQ)2sf%J&V{;xuA$okupE~t zu?;Z93M}a8HV2`^x4&L>!l*Z;(~qmjY%^HrC1Dwczyw|_`Z}6N>GKdl?VUQ6>p%(p zCXmVJ?ZRck3TJH@g5x7KvmyJVd?XZ{w?Y$*upQC0hU2&pHBK-*x~cebi_c-fRk#fU z{#GO^&zqy|_8@?a;tYfLOQiLN4qEjmoEM-6!aG?3el&?;Z=UX;D4)L2o8&>Ol@AHt zkq~S~e=wW8k=`KeV?9=4{PzLfbN%^{;$NkEMs-SWrsgC1ew7`h*QtEce-XX* z>*49FfXcUD7xSw7zsdocdMw;vfuJOcP>;lEuuLx`DIHa!!UX|N=Y>?eqpD28GUbai zU>!x`xu|enJ=oF`OVrHcdH@qRjKC&$E-C!Z)?8kY6{Ue8;1|+NLj1bf%Na+6=ROTh z>xmXr`A3GrD}q{azEi$9$2u%oH0psmZ#d9ZfJFS@MB8E&{TQZU(?WZdk^GP5$F zbka`BXm%GcT%BHPs|W3rD;w9%0|Qt~)wVA*XT_+OcHMW}Ra<;*X4YQ!opiO8<=DF! zzFoWcsMVV0NAwIorF&)F7KZ?dQ?|aoJ2LdN5;I>Yy$?SA*I z%sr0inR);1>76F7X|~dbX|)HBYJf#n$gEZqsvDmcPuqr7-yVLl7mKgj7(~!+-;*w3 zNEm=+>vC2cN>$21Y5Nl%@Q-MK2cFD6lT%k$R|9~t?n%1G6hQ?8=7~}$b+YJrJQYRN z4Hvno5u;l+CZ^kSyC%Zyi>wH61-+Xl!pvZXaG;AYLQ$l}tfo zEnyOak`Pb4MJg#-)I{QuoL;Dblvby|hB z6hO}$G_=kww7`#6n|y4&Mn5q}2L#8ZTmy^I%PlDe8p%qhK2fQa5;sG81Eb0 zK7Uy>-&rgFP01rtVDaB)I1Kv>ci&zZVZ(3p9|nLldJ~&&b_r`=%i!gko|+O7y1ZB# zPC&eCVj1-2y~!;61&-^p?@XHdy}Rfx2XSVIi7cc5Dw&kMQx~CAtMkl3P2_G zeAs^K>1Ue(K9m6-12{kXc%c<6LgPwwYmS;aS`iR15Jm$4k$W{*dL6S10DgJ5`MLy& z(v64dNe~>;h>Vm&;B%w=FQ3?I(nhxDDBisMQm-SR8Z}@9VvI5KBI#6`VuZrl%B@kZ zM8Sj=O^U+2-H<>=P2%N#qFP*NKiBJzxPcJ-_AU&|xP+Hn^9ifPBJ02bDCdqwmWy}P zCkWdxw}w@+3xM9kfQHckI>CXeX7fdmjwrS=$Dl!-ndw5J=jIbQ%q;S={3-!cTnVE( zgzN!m*t)qz`^~m0<~LYnV8xLU3}BhhCX?Uo_&VzT?nfuCC_c`O7rYS$I*7MP;cU!4 zO%gcei+y(7t|%oSLP_yeLY+w*2%>}}aNfB1Kcu}^R1?voH#+H|hXB$cp;rmLYv>*6 zP1I0Cief-O#Fm866+$n9^xivILlF^B=~6Wm1r!wo5i4KL|J-%YIxqKoxOvK2^DxQm z*?Z6YDuHewKm(fVO++c(EaDR5X-MQlc`unm_yY!uZ%r&iDgSpO-RO?UxB<^O1YLwT zjOTj3tJ(FQ!y^$A=gl0EQj~L#;jL}_o#PQcYmq&NU&DN5Ns6!o%@u8v?Ni)+)dP%t zAQ~!rQ?&b!RoQGide5)p7Zdq3!lgkryA*-zD5$IE?7J?I+7nC5Yi@N!K!RHvbVLWbFH~- zMT@`MA@@7l1zWY_R#h54eV&?e5HygW=tO-1io4Wfg6}aX?2^7QrzUGjip@(*?n%5e zmza8Vm`D>y%FszNbV~e~MX`cYb3Cv^e3HOW!gPs*ZzB5PH<N_-opWIK$GekKCOBIb9{4>4d_3|OCTXF3j9nsXly zS`}Y}njI z4GF3e7UtfTG;+W+9-Pv^qe3z22eNE6C>Vf3szWk9`P0h5ncJoz8uOxnNUY(5aJoqXH$y!s#%8^BWzuveKtG8iXbe}^UFP}6Rx-V*4l zhg7Z39i|#xi*QFNjOzgaoWg>9O42btvTjo5rX}oscvSCEOQCHzd#XE+B#F!i$rJZs zWwsAR(6!b(uT7#rSH6++Cc(Z0=ZdeIz`hiRX2ptFqDMHx^Z6DIeCi$p5hjcVt`F+ z0&>fUWBNNMmRgqfERO)$0HmVUaFsOTZ#ppy+SbUbL)+D8;$Y(Gqe1PQ5fIh`+a!m(Baiy(OL+;2CQ zZ>fwCsw>m0tMIC;im9t9sH=NW*RW96^sBC=Kv8N>Rm{1*)4CqBr>bIFk095tuH7u2 zm6Mi{ig7@w#>k>*4M0i5=m_dXaQN6r1BXvIX$1r&LZ5$Bv7Vf$Zz_U1g)5Zr;~Jl`3*u;y@t|&Dy2C@5#-qVbH?sJd{zXa{Uh074&rJgVq?0 z;te;QQogKRc)#@S-><6O&v$RX)BNx% z_h9(PuOEQk9RKFq+Q&P|hdYh?JB>ePzy4;%2mW6^FmVEK$3G}zKvyeR{}}taUZzn# zYn$cg%pPUqPbFw3&GWxp_5X!b0Z1qSfVS2;UGjmTly8ip~ue!GJ{r0z|x9g)XC(oY`Dk(0jsc&6+_tDPY zVSQugLcoPEVi+y6z|+I)<>Z1SMkX#X)7;#uxV(vQmhkX#f7s} zg-KRYk^pmZaW$EvkVtL`RNkUFjGO!Z`o~X1O=G_8-EVvQ-w%Eq{`_^5Swn5~KZ5}Q z9s{=lbs+vfe+hk+OdIGMykAnaw))=0#QgmE^HY;kskDr?*7p6q{m96xBQHkl>l)r~ z?7Uf8%grlHNxmBrLVVEsGys~9y z>y(p~`{TziVnocVm#^P%?kv9hSXfxZ9BlG!>e+LC&xS@Co7(Q)P4DdL;o&(xH2A!% zwDQCH2TKc^z<_|+>DjEz-0%AbEltf!%WL-vitgS`d-UkZ?VBWT?{mvb?<%Sq{{8(o z@p|IZ*TbL-!LzgT^r!ua2}w7u-<*8)dh64E#Fgmb=ff?{t?6l*J>9+km$~u(?=a$s z1ORYKTzo=eQgX_jyQyik^aK%8(~MlE>EvFXcVTf!X<2ziWmR=eZC!msV^ecWYg>Cq zXIFPmZwn59J$Op*>mL|=HdJ!@`ADMFOR3RuoaSj1@M3&O8LTfP1h~u%Iy#D>gfxyN z95)1bR=NfGPy&hul9Fa|G63jn&xU~qu<7$2TE;h9c6iS$ZX_pxi{s4cz)$37DaSa@ zS9t77JpYgPKXMHYoRwr^X)oF;M{{w0Bmd?7XJTnr6jH**AWDL+oHtd=EX`|F;=Ld7 zhGU<><(SjtJitgU;dcWxP!T>!B^Tm&fy15EPq{+dB05a!*pwz&KXr^APF$fNp?YqC z>h!_(LaPtLr>{(lSH(PlP28T>l}k*gp@txB#!C7yUT(tA`?oh@ zW4cwi?$#MvuoFy5ju6gZOK@9wEc`A1m*dAD@4B^5W}TScl;)r!y=YQ{&wtfCyD$=H zkeF?X=H0B$lslok+sEsy0o{%4-a1M7e% z!1|vRnn3VrZR<`clp%?k*~z_$1syv$JBkq;|VkxMOyojdo();ffwJs-Yv*0^EcylBVe$awXYxiI{{ zOF9ft1-L;Y{~;bgAPA5RMx@km4fX&--Or84rcf-E7mI2v&`#;!&zGXak;}^Gh zI-xbAyJJm^-1+P^P~yaG8Up|WxjFd(0YT>+X_>QMflsp>QNLE-GEPTDo^SZ#UYVKx z^PADV9{0dDK0YBQ*MX)NjIFItR`KiaYmFLKhh}CM0zWMEJ{tTukPVo0ZH-Q7YwJzz zegC+<#~h;e_HF@pH?y|6S*XQqQmJrO9U#N3iScfq+8K?@tZjT*SKAnoT&H;TCv#h= zJs3BMo`#!$WL^f^5S`We9vAf^aPe_+^K4;JxuCGj!s0sv17lIi#!HvN?XT6&{P}(N zE|bMye7?MMDSz~8)U~!B`;V5sF%!L}KW*1pc`7JrY*j!hw<)5^k364cb)Apor$kvk zABdT)1*EP{3EP2sylxKdS;k(+TSPqtGy&c&#OBk1{%3&HJO6LfK&evixoC6S-npn8 zp|5Kv>jO=Colq?mZ_fK}2U*A!yue-^vWhA&dXi;Ye8_Hi7l`SNcsBqQjyrwYZ`1sS zT~>z8gBFS0?}B<)EzYl7?rl-8GQQ{}P)erOn|*a-iX7kfCii{@W`;wHFJ0=M1z6s~ zy*4DMyn_;y#bE7G+o0sT=wc%C!BczM273RqT?wr(J$<7Y%?b4Whe~bFDYj8lNvrZ$WzJ24n+J^&0fct{aY|9^-FK1s@d2cduUIH7<4&*G7tL zx|_~qSVAS{N4r%>Pe?!iacpa=_&knBK+GSx@2>VIz&2p`N=)`>w!D^G^fiWmYy!`% z6FR*bL9b)@QsK&~?evkPum2*3TcXBtM8ob;4~ae_3v}_1Cj`06!rtE(Y?tXf6 z>gLniANs#{iy8IRYM&z>N=JyBcg$~Kh+M}#&QQJ^9lpDHj5ViP^JfxfAj1 z$r_kMD7>bOvh_Y{<-dpr$vdO2mZec}%?;tJFO}y|X-Zm1angJw(&wgV=#OP{buHbB zOb4y4i5ptUJ?m*-1rJLTJby){r|GC{%flJH7yQI8Kq` z{=*l<8EOCpsrzO02C><895Em~8Dm~?Manb`3*Q(Wgs4_F01)VO@Hho&c}GDJTGHkf zhm@jWjM>&-e8__8;#P)4VyIyNZ~`}A%89V_rHHw?kgWyAJd7x!ZkxM!0SW^HQjpl> zPT;F4(QOue55|Bvxu`@EV%Hi)!PqkJUqDw9>0^-ZB@&5)C`)o69~T~%AS{b4jFpN( zmLWKGHdR}A^PRZK&_TC{8)uXcs8lGC7duQxQH?w~PcQ&7RECOW0meE|fsZ8CKS30X z%O%L7I*F+G{4h*!ctLi&T0GOFF@03SR~7dJIX9*Hw-zP zE#DkPZcaSOxAA?>kIP~zHQ?*tAB==k!d9!fm;e3TtZqI*lWhF@@AuAgmD}kUp$i}W z9&R)r{re*vQicDao^!OXjsGWzyA9L8m~sx||GfN4o_{EQ!+cV3BoC_2tlvE^l!(uA zA!0xcZ$ti6_pwuOqyRmUSa=PZ^WRrZX^m23v9Yq0V@ureF&~Z#0(`$bpGzw<;6niZBW>@rTl-665K(EC`t2TW@2sL0pH!cREhVK-<#tjcr? z)B!3@zGULIEy)~Om;l!=1UPg``Zym3KF;TUW~+dtu*>;zsb7obQg(36TGvYeDN0d%dPO`H)kTfs)U+56T zk48QGLMalshsl{J5f4 zofYL$Z9VhPbg$0iWt~fE#Z1#dQq>7iy?a_)=|0J|;g-gD*3d#0G;ma?>+L(iOg$YG~GY+D6BwF1zW<(K=NEUZmRsnC7 zIW&tLN)&xb?rwO`b%Op$(h#8=fn9eBLb>pidJyBOPM$C@0w?Ix z=$Bn^!qCYFOWZ%{y+c?L6V6CY8G{lSrR0tj(;*D>fIPWN+y#D_$)P#v=+FYG2E0Gx^B17+yc)0# zke&u9KtmH%y%cYV>Nv3Jrp6@lJ&*+%(v6N#YE4SxC91up(mGDAND$8nX;8hLF1sxa zXtZq6)t1tMx?sp(F&VLdyAhXiUThQ1w&$Ck%1{Qd|Ct#&iDm#SQ78};*--y7tcjHp z?Pa*_57WPNXKebS5fbb#yXKG^!4nVg5xe+5>eU6cvYPxjK-Uu%zj!<^$l(I;Jyq}L zi70Ggc@6%7^>-mV?j7C+jBnf+2;)?y(2|eWRtQvo$U4TxcMg14#v_&*8P%3qpusyc zJvsb#=mqb&HyR~uy$@y1b-c9L@t=)v7`E~8+U}cCzkmB)+%3PToI+@D&BQ`*0;WC-|wBa6yqH$Nq$LjxqH^6dEy z@I7p9%7Obm09_~IIVgsxmQ%h@qWbi)f^}! zaBrC+ei`xG?IkhaUFZm%7e>Op8DM=|02%2=QQB@ASQvd>ef`Oz@SfqPJ_@N7Gdc5U zp-C{%2M;5nR5xXUZD}en_&GmGWUUh{Y8*#1QCU6)dhf35%7FW4;7*W^yOc1ajMVL} z-1=h{%`bbpH!Q*>o1)M|0bhv5ReR|5qgiB$Ymh54HEuXd3NOEh^Ed3@D#`agZd!q2 zQWP<%M0dkYshw_VrNb-Ac~s?WhwI#NJe~F~(QzJmancpBA16Y&DnxC~F2B8EY$qqJ z_(NpeRD^9BED|2ByWu&m;R5P+u81?f7#(kR6~9V$X4!YVR_6ViX-kQa8hjRhDpF#~ z6y>dfu7w&>k9HGj$fP@c{KG4W>Cs7fc}ZC{q6KqFpAS&?1(NSh@E7np>U|IM_TVmC zbv`um#%l|0`Z)qwN)ZC0y8D9mxd~!%Cv7*-C$D;Gd!W0Z@z$H}F`Yubhp}%=MBlYu zHtQh{CsOQ@GMQ#_CGC0^*~yyU1%IhVE#;()fi4|Jo_s+ODwz|urJWy}FqDoLt=f~g zh~OR#KzUQo%wfSOvXUzU(hfiVtw&_f4HZQamjY6sA`Kgy1=≪`(&ANDxU9WG>G` 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%0DEh??1dJ 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⪙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 z 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ð+Yu8#X@ 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
v8(}w&lelV?V2c(uUaAlG;k_bUyMAzFP-x zO!!^B=)C^@gZVV4sg+;lXJ0IP4<@|y`bFAzn^-}$SG7-}$9ArTF(iMd8A`2e>Td*} z|9U1-TejIsTyB((c+2tHu6>s6ja~ap&C({}yY9%T+B-p^lkdWm=XaP=JQ{ZUibAfm zXj4A)#x348V>WU)$UV_$zMNxX9q3AkVm?2gqa*3QAR&L0XXUQ%g_diX0iX6h^X4?l zisqb({&A!273_y_!CCWcH~dJcj9sm_-g)<)iyP-ll6c|+=PKQyeDnW#Xj<*f zQGg0T&3&8l`=n&G|E@h`_TAY4UTk;LH4pdDpfA2!Rw8ec7P}JWQl2>PeHrinaaTlf ziq4OnLhX*dV0f|ZY~aMJC*ynoO)w#q)`ELlC`Pf9pUb7o;iSoQhn&xxN=OT1)Zf^YM5SWXMfUa zSIe}|FrzL~Xx=IN2>$(x>&-9iPcO-q^S&=ulwspa51tldfNlu0kTQ^-c9HqHYgwJa zhv<|J?1y zXF!fNDi%EMtf?DlnD$;bUrhJ-X|p}UtnI`!sfIawHxsyr>127Qhlu$f0VU0vJp?*$x3;iFlLN zmn5VP6BoD+`FepeDhm*gq*$E-On;bAZxnL0j2lXr(!|`*Cmzx*9`l6Bz|AbX9s>TK zFSXhsz$aumgvimH5K08iOY;#FL*1^XyvE7Q96N%OkXA?^JL&6uL}mR%07m@MwQ{&D*pzBr}d7<`Ih7?XCFe_$?_j@ zLW0)h(xDrwx|-kNkCft0P=XAdfUFlIvTXL{?x}|WUK*j_uhOJW%w8;QjLZc z6j9~nQIGrIU2Y_|zUb47X}LPglOzwjFx*l7q)2*gk9WUF#9A}e_h?$U`9?xQq2opF z&2JH>_sdSOaLmv>Q_Jh1%Y_hFAH^}Q%g-nR{sTO@xYp#%OqVj{=Wgtl=clSp->h-B z(_vAaTaz&`hNkzV8I~91sDjo^QAtfGh-vjDSnaQh; zr5+&8oc7s65f}L^W+GTrm=IwSD0oi*Xhv7`zMgvBGI>ta~N$5afs zJ)}?L9Ed~vBc*eXUq-_&UgOqfS+ z4iR-_^Qd1epwAr$%{&Q7je|((hJ=6g zmuuYK*B~=$rtoT}ifi6c)J)USOt;j`aMR2T*33%O^m(J6rl^$NHkj9RKKJdQ@e9oq z#)3=DaY1+5QyBT@J^9|W#9`J;Zt|iHO;NxUs>~>}(VROFATa}gF+lnn0dpY1{K+VP zs>l^d=!HeVn$T1a2QHgx;SIua02q!8$bjDugXpSZts^*yQjeBBI_8nx@hddCI2Gg@ zt>Q5Ma1Y{%V>EIE6z>TEH#BbG;q2OxTaqxj0j>F!NId``5JWXzggxLv56;D% zM|b3)V_47;N*Y>#Y0nmyd_F}7i9@TvNf0hHa6a30-9Sr(9HX=YY^02^y^hf{mB`(R zxX2X?14xT6=?p2OU|)r=X^cfv9Ku5{k2JJZu9c5@7S;q=H@ZvdiQ9x1(>3YwNJX$V z=~*_>eFu-prr8VX%R}$#=dbD?%yFBx4Q?fB7Ba?@{&wp1+aLY2n-Kk}R75d5-Kakm zU<|w0+nb!6noV0!RaH09j0`Ot9sl?d>DgUeRFc`hcqDEF?e2o*KmL4c_hK<6w4p9_ zvKPG2G4FbvNX$AUq)H1b(7ogfV%MRO1v^`Z@XXB3_3bO+W%`86&zJWBv!$NyJ|Ayi zCN>_Ch>+^d_ePJZ0M{P@g{Q)<0JhvECnckp4&QX6sf_lfrleg?AKU%DOCZ(p z9OXv^{J5-35EGLCyM`Y2OlqiW){cFd+1z1%RI{gg#N}H^5L0r!zWlf`!_O@P*BM)bGI4B+d1uI^UABxpg z$OI15jK5G&AFv-A1nl;LtR6MJ-j(-jID1y{!eCcXRqM^@Ww2%FhVLg?QmE^L2VRMf zj<(j-bi9+bFn38z*7`boE_cs2eZns`SH$N>?&@;u>PqA4KG=#4p4T&GH&?W5OMLy9 z_H0dMunRoL=W|@}UtbRKpM!YscaOeFb*u!f@`cdQ=Jp|Fm+N97?bV-uDpA`WRzH%I z&;*Pgxt?oBZbj?l_Z+#<|##|PTS45>m zm;gzyfqZczk1l;B1FEH3vp3->^M*CY$Qa|$Jt(Kp_Hf`Q(-#lU>80?tZV$?PU3ZNX zY<}?N1GeAHRe*4}KI}~+L_>lszcD{_Pr(FyLFwD#voO~W)r7qBzz0O&0pCGho8Q{P zc%=x-F{<2$w;!M0(|__`kHY`AYU=-^Ka!i~JX7kgoFwCNV1 za8Satls`|s4pl?u(~KtjBQVB)b`yH3Udm*D4{?al{6kJS(muc9(_Od5znyyh?)k}a z{*rz5UMTW};1R*kI0Q@hm&YoFTX+Z+0&@b3$ zxQ!5p9;tzfw;5qh+57r1;EuwXT0dJf({C;}{g0#Y6&i;0`lM0E-i4miD!YFuuy@eW ztu7V*r1uRkVt8i**Fh=fO1dKTEioC;2eJm3Wo1ZJ@86EX%c|CgPMoq>?-$oj8^xp9 zgn=q09 zE^cgW209WU!DYx18E+9nIf=wt05}wo&Xq=og+H>n4D0?wHsNWv){0@ykF{p)X>jMAV&x!3;3QFv2{vB6rn zxI$+Vx)vW%#T+5Ci%mP9g{k`)ON4c1(whu)9co$ovzyQ$t7|o`MzgXdoY}cp1+cWa zfzK+y9K>k!2rx?pSrpFTrTxQ?p8poZ#|1g^#Un<>^jo~?aN(iH1HBS~-xAD<1z78Z zYDCUU@i`60Fony(Pk&ne*QwXH6BdWa^ZN>t_#| z3D3gE%U|Ps z9md5I8y<+zF_jpo5?+BPAWEZSBd6d^3C{nBV7zh<(o+M@*+wI=uU(EYSc4uOl4MfV zQ$d4QO2*}I^Y52C-PS{3$1X2r(Di!Sk^$MunV(NNpc50MaOI<@!a_oSa8$L z8{V-h9v{ghcn?1(pGGg|`!x+7DlI0a!A@L%cdA(6j89>qxLOTPh`HBRz@%wedYM0R zbT*>QeoBRxLW%MxhcT*Tsi6nB!;d(1GMts`JjRQOl<&cz+$CQ!|B;P8Gu-L4y9nXD zfK5HQiDmYz2~#T?Bd7~E7K~jlK6RFUT6NL05NNt~IzkI^V%P@Gip0fL0H|7l7Yz22 z%1ZgPsAgdS7Bx8t?&mtRtaV(!_Mq!_PwlB^CGP^EUc-Ju(dJW>A|(tERUgZTLd&yP1bt1c!_)}n-t^&stSH~F-4 zpULa+q-QFk+U@}nfp2)yw?`iftRxM2&;9tYt5^EO2KW+wF$~2gj4t=KtAXdLGrg-IlAw5fa%VR6E0}m~( z%{ce0*lvmLI2N)@Z%TZ zaB)vq(a@45Y!a* z=Vi|p& zEY(9| zu|vCAKMy!dk`z2ltV!bb7#0&uV4iB|z9ceJ)p()CFo}cZMhwD`ljV%4xdOpZO44}C z!(@RNF;ZUquRw9frk~Y#UI9z*AvCWe(csP^nT#MQS3yAHsl)G_8TDVL(X* zsoT_6f#K>He~k$^0n9=`9sI3__L&-63gg;ZlMmefYT$c9O^- z4-->>PU?V$@FX*TqaqZiH3Z2lkZI;P4YKVlut`Sk*1$Y50Vkc|21KYk6}IG|HFGYd zr3oZ50KcT&IQ5P-25w7=-}iu;Qt(Ptm=uBKl;&miXvADYhIS#0nUb(TKHJhOH9mIR zW(g76dwU1N!gvXhmZ1Z6HXyFs7MGhbY{SU}EdvTmxe$Du2G9j$+hu}mD1HyevP6U* z6Af{cH@Y(6o)~G%Uxr^;a}BD%)5#-?OjvMbE|NF3_}1WOI0N)aYAQz()#!vLrL{h1 z=zS>F2_QnPNg7#cMv<{B`)m5Vhhlu1JS+sFC)4zD)M6dVZM>XZRprSp0@1|>b20DC z@^fVKT&Hd*(bSbNh{>KqrIFg`41TuTIl@FXCMvw*}M*XNH>=A<~Ibo3GtN>(?v`);JL7QycbT>!4+r5h_!4ViY~ zr!-TTJ+y+(GGz*dI+K|fuu(0JaLGfF)jNggy}a1z<3d~ExbJWodOG_xh+7^g_r7|Z@Lw1VUo><&aRxZ1fx=&`t6XCAppr_pM=1e4Ls|Xmlp+6K)`hV(r z&!{HeC|-BcLkppb6h(TMCQ>yt=@5|)V(6iWfRs=THKB(l5fDSKYG~3#L$7LRf`W>Q z(kx&_&_6flmUGTp_ulXGVP@96GwBjdvKS?7(0dhS~MjRI>4 zaYsZ`6@3GUCcYMOS2JAFOV`uN*K@|N~xGmDP z@QuZ}M${{x-3UN-yNqxto?7xXj_&h6g6?c-Bz76EXoG z0f547Tnd;qS6yHPaMZ;4sN#Il5F;$H*P(Q8^Ij|(8q3Js(#BzNmVZ^2O4sc$Zdy4%wxE% zIk2DUcVCdWWf%}WH1q@#*kEu!FC{#aOM>GyYk%etMIbWt#6281rHOT0Y0eCY9r5mk ztQ!h1P{~>4U3xfQ;W!ygWrIW7o~}&6in-<)_&K=Ge%RIe9@Pd-ct{qvKGEInhysso z?lZRZF{Yr5(J|ir(ZNJ+V;qzdaKF$IH@j?psi)tUf>O%5p(6x7r(5tLPGD;p)q&9R#TI<3vk!E~)K7rY?Mo#%=%8E$8rdIzq;W8h{ZE2y{f7RqLv|cm9 zSg*6&p<@4KC%x0+@rBNZ8#YRZZO5k{3bfo7b;l!e+CK8Pzg{1`QgIzm9sMv8{5cv7 ztFS&5M6-xW^jES{n9dqiw31mVul&`cEMYUh-j0sG?aas@k?9K#081X>$OXro|2+^; z%CdmHq&y;6I+P%26AU{#LOsr6G>$+O@|l?$z35)t(QFlA7A3tXYX3C=HY5fOG6h^3XQ7&D7 zx4Bl|Dz{uJDMw->4A#<{=k*)3g@xmsCcK=JULH7&jQBpba%9Ua$3qcYXb6Y~lcAs_ zY0Z8BLU_G&kHUS4X4otOb&G{v033fIVBzQ_7YzI)4q8Kcwqo|XYQv$h(8qg+cPN+6xT)vZaoSLbcDUxy^vr6ZnRTH=!t{;`JzRA z?j90)G$$b*XSfD9>-RA6b>&ZRN`!`o(y~}zS->mB50cVV)Kt$cGrssCotuV4NCIrx zO3AWx|2sNhJ5`OQ3$P6gH@O4}q96`OU_Svw7!Go}XRvodoJ8pdFKfVr(@Kw3Dc787EJgYyW%F786*DBRqs4tObE zo8S=q1B*&w%-3U>BH>s|=j-P2egT23qrRE=Pu~g?b6X z{KLj@pUOLKw{;*xGSe+j5}D3u1kcfJpKzLS_aCTPH0%eZR~EyKhrZ5`epOMw9L#Cn z@9H6F7I>@nJ}ldUC%)affAV1WPJ>CCZ^?bNSFSB=EbCK4uzjYrmbonP0(U8bjb?Z| zLCy8 zFx)cxNy}l*>Ph!v1zP-$D6v>fV*Yh!O+V;`+el$o?!N%rqv?AaXy}$7I5&yBn``3n zE;*KwBZ-B}uE8W%An|CJAu$t1tqzut87f2k-S?z^7<@Vtc_j?>xl3+=plNOkKiUS! zBM?t)?g`P(y|75j&-XTtk9nEjPxkeG&JP-A! zH^|o??X7ORf3q(?y47`7A$E(=sI;^vJk@O5%do}5Jh&Rw&LE5^aEdsnZoGQhD(XoPl(Gh> zVQ*fhTD(=!~(IrYh!R_(6zO=&qStiOwg^rpdUo;tzD2Dz{ZNZj>S6a z1zO4-F(9d(_TfMoinRs23crB%@JI&7h86Av{8ET`H?1A{lLGkGB^jX8N}+d~g73|< z8eJ$B^HZ^phUBGcQ2=Ybf{zxrPV7JZtlhR@*%vwg?H1u1bhL=uhgaHiINh>R(SQ}q z>3l6OGt%x}v6{CpqC^&m#_@PdJ+YDG{l@sSJ2<8TSbUYT(M{XB)X3tG>f9E$9a~+y zLO)C`POzzX{E#+R4L}`h_r0HE9r?~=TtEEn`X8c%Z85jxxgW#@$&&)+a<(HnAlWO` z`n-)MRfUaT))KDr{-mjYfY$0-ni9w# zHDo0fPH8`Rt)2JiLz&M#NiIF#Y-4U?&bIozN+Ee}p25@_N$q=tluEP_^C}W9-T6QG zBj0VK6TKKUQ}I;(2y=065etUwkIcQiy2!|H7IHVkJ0eO7k#zx8S!4iHPC-GW-W1eXnXGvB&} z_W%6luAC(`u#dEUF`WKtg*rGE5sW6m^DSf zWw8-IAfV-vC=$K@HhLF!%$sD;wLhO!&Q9z+9kU8 zLt-Y3=#dwU+K1+IsPxDI>(+k)XsLn+-n!XZZa%LHAH;6?URUXPl@PKUi$7{^sV}pY z`Ko$=zj*7Hu_?;0_;r=Ki8bw?#D zOI&C}G`Rcxv42r71@3HrnY%u|7r7vRgZ=u0j>_1*?%(eX8EV&Z9gleHt z7axZ&d&NfH7YoWge0hTjB|@-qsQc{6uvQ$*w%%SLvDi|+MpO6AswAJ?Y=*+sSSyc@ z_ND#ney0=vSmEH8sKd~Yn&j9S_pxeV!q;Ep_VJmz8E?Tzg8!MjN3k{`{+G&E*8R^8 z{ILnkxK#1J^CI{$K=mlt8IaJ=b~8H-AA41Hg#FJ)ReWoeRk;QFj(1fW5r z@rGP+*fbf3Dh@xqvDg8Ye8HhQw%>cG$8R=Xx~`hfMydb&ES9!3Zh~jIKDaqh~xJku)4LmF0FV3uIacmH{F{p z2iTF>WAIgV_Jb<69abB7|v)@zTBU_xjjV>&l(mvLCM#dg0` z;K4b+Y%$T3$)GITdw(_LYxIc-#gMltU&QaY%DbR(;#iU7{R;{wHbR?(-~esEE2erQ z=c;Iln8|+Qo=(r(Ch?2Vreo6tZ z)9xQ5N(cV>sghi0d}OZQdXnI;*}y7c`d_D}ulj3`yFR^M-=R`~zNka|{ExhzJMs$Jgw#Yt>Xup*hM2=51&9^R*{z(oh!9{e$1W`h$Xo#1SWlN z6bsz8fBT!-oX6Y3_IX=&P?-8n@l0WtgyiuJHaF{q**mdfJ1N@)Mcp+^&X4<>GVhue z%sklUeP7Wwlvnberh$MdVog9%TbjyZJ$+LC^M~pK+qI`Wy`zbtkqz#zcKl=dU;PUu zjJv=7ULP~?Iq_P;hWnetk1>z`{kuj)c*GoWptYSB0Kh`1y{donqcS_s#quCNo`*A9 zdCg&**LMnzDGE7?Ww?;Y8*MD=*l6srORDhN7K}kLoc7!0rUrmOO%8)vY!y} zTLW6axRZzy$&V1Em0vlf^yK3jpc)6E}I&XLCkZOFyo| zEQ(oHu&-uhLF4l}RwK*n+Y@6`gsPN?(wP?TFaSo7_w|19^S7p77Bp~7wux2`C`d@k zVXsb23~rW9eIYfCrRF!Yy7aM4buG=U^YhP-nXXrAZf>QcqnFU$;v5t8T_+>= z@C#qeUqAr(^5t8M_dQlC-F9Ylb>+>@IMedp{f@C&z@|FWulMlqZ*NZ@*3MobB`G(r zaBE{5?O)%uyk|dLcp+^nmD)Bj^~U)EVeZp!{PHjOjn3xHaaIoHBD>YaRpz~kS2p$| zi--HTpmmnv#jRwH9Vzg6gsK_GQn#EY^}g)b{%P)fB{%ckX%(~im+#P}-L22p0hx4` zS=P*^=kcSVvInh}eVtc^AF`+lOfH!Pq3~bZ4=nDkyj?waE#qQjIg72>tOEF(JdPKWLE!Z)@MR4+yA9N;A|k_pkG;UL z#~&M25oZelwP@bnd$OMH!$YIOF4RkLO@K>B2I0Nb?QeKSl1KSf4>4Wq-YMWA&eZ2) zLf^Xon?Cx_mGIkdbsxmFR>Cy8s!n2n7>NCjhwR7Cm`A5al`j1qYTktC% zzQ1^cG04E2kN6sO#kce$aPr1g&yhPz3$#Xl zR*JTodYj_i(B;;oOvI9 zi}T;!o#xKo&HtZlZ%fgAsgaTYC2$cJ_=scy6obcQSOS+xUJOdY#BHhq8C?IQT>F?7 z2v>lcsStBF_l0&gUc7bs#<$21K{aBPqCc%yo9IG-%NZE2!Bp4IxXh_I_L)murwSyw zz5|42wpwhuB+CTeeZgf>tIrb|(y`d`<}pwCs*ZW>&>;K|^rk?M0$}KLMypj=o-R++ zw>2a#O<<0w0^w&6(23K?wt?=yr2EBH@n5yl`LM-rPjCG|W5UD9TM z#aUbK-9?*EXC=&XLe4-^Wgjf_rd?mrKfW~i+g1`R1!yWbgJK@=r+x6en|MCoQw~2gx$ad0Xv6*+t9X-sYF`_ow^dNhC zTwKJ%q+fZfkJ1f3^+|0C-1JwKVI|eeoP-*lDtHQ;orOMQd#g_oor_wJ7!@f<9ly`j zri9jsl+r+7hw5+MvE+Mla3tdXncxXFg z5q>+Nd-+(?k!1=Krl!nukE@9j1bC93be9RA{<8m`Vy5zEozf9jGgE9VKmMBii5r^3 z9)wO?z10FYL%aC4H*KiOuIFRWE}nhPhU?#T`J(~eG-MmnA>C-m(m*d*zX-<-2r|dw z47n4|{k`I{d}$aX(KwiAFn-bm1rX13y9jw9`5$2f0rCFdlG{nlKbzfO|2y10wn+H< zzuDccScV*Nm7N5fr70hj*By_Hkzn@#G{`>0mqTP?L}>Fq_SN0klWi|Sh(=7BwW6f# zj`8mYFmf1A8mn;6(Jj>jxqYND}bg#3MW-b#-(qpKe2;Kp{v4YNUOslhJ`(7 zNIR!B;o@hNULpC1HlF1F7 zJrgTp?V*(8sX4&=dseIL`m9&6V;$Jk2vH&Zw{6Y*j__wEX;0_e;s+?7#)e@Tv6@ z_RJ5`2&24o^{#3SwnouaAf@GC_7wax@*+u^@8$&)M+JkD<{5S#hqlwwQcPjfGu}+P ze_8;l;tn7-IetGBeTVzvNy|Ht%*;(btB%4~+jD*Q<|bP!JwQmAB@=WR!z0P(Z<<>{A{ql>aW5ANQ*aKUONWUBKz zZ|5Rj^0>}9WNl2l0Ps9`{L=za%;j69XN0f;NFrj z7OxO8m#tk}Em4up)D|*F|A$3ygT)_#{#n;+BkG!nFxduOTmv>I^<{FY!R!#I=HtC~ol)=!ABO8-VdhdBGz??@0>1i5$5XOJTF$ zr3_2A{P(;9ZB%2=On6`83!>vw8bfAt$9!i_e8!t=xV2M9&9B|>+IsoRv73hKYBaOi zwl6xf^t#f{2Aj`8?uRdzpFVidbh(`v1;1MT;r)Ygn=cXNhtK{NI$Rc61Xp#6 z08*;mtR~8}5WCv>@Pi+i=TEMsKImsNZR}}TTs}vauFpnnn*vQ)M zWx4--C-kgmOyfjaUX1AbL_O^~A=__)YlG`DN5{s7B~jsW{BJ*~=J(D7*nTft@%V_H z=zSXV^n2yb>if7a(e;9!->UWW->aN9QVO;GF0b)#OD#WryutQoet_sZp1szFNXE~5 z@&9&)kp0W!w!hlT|9!&S^}k&5j8?%U&rzQrTblgu=e@&&FNA0PuYW)N{pir(>(GAx z+I3ngY{Ir(;_KS;ozP!{h@{<^8xP+|bN`t-d{wW;=lUp3?Kget_D)J^!kYXY$lRgZ z55if4Et_kxk9TtB7Dg`BvsFW$?EqYUr&Cd7%(RCa!ruF^|tZr#+cmVH8YHEPJHCGkF5J+5SKshjA^e)tTEtSbo zvJ+3UqNA*U6e~K&N|mg01R$+3U}ehnqDF)h6y$-1s-8{keV#-+Mxgyp+4@aTL8k5n z=zQPcSy#1oE0tBIvzgM_5*efjI-4B^6^DhM??|O1kvnLpjw+l;xxR*liqP4x7_i9h zSv)Sy6o6S?GBQ*J<0oPimgANQF>2c3InjZ?!Eth%Q3N!BK#Vcd&I@UTd@#>amgD@O zozU8l&|I3E*qxjGK)#?}OW(+qx}7t>6Wrtf4h-za@AJ1FC_G z2gnJuZZ?5Gg;-{IZpO8=c6Ls4#JLVoFP@60A>uIL85)G%n2H9tDhc_BEpI_Xr6`>L?Uob5w9X$*l$rvl zt`~97@MYXD6tEra(Hc}`2Gm9sEJ7z)LAcOpV2uG?6EBZI0}t7_Bmu4*Hdu}Yk*Nxg z1V98Dq};^RhY@9=4M$BxMbP2QP53$jjQ!in+R!y)L2 zJ|F@>;hEq_8iZ<&SUF2GRfRJdQ1Es+0Sk>|zy@5Qb~u_OaC==9(ngLC--C8c=JP3( zdo^UkU$WK+w0gZNf>;sBfVI9rFafU1Q>isgxeY>rPa0GZNTA(hP1g|rV49YksRpB1YJK<_@4kp5C9wimlFmyx*CYgvtw>Y0H1W`D89Y7fz5UnvN z7~)+H$fups%DJe<>*IbmT_V14JyNEG~XBex>`OM2<=@)Jro8^>9m?Ds4g9({Zig&qCwWO-g?@~lzyw8ohwqjwquGj z{B54o*fUWO#mxr|;H9-<7oIbkXu@@C5%)uar)dgJh8422_DQJTg$_GpN+1T9FUjOc$ zsQ?mYgF$&PINOf{N|;WRA}5xHPy*^A6>6&lZ7(CTa)RTcNfI6>&TClF~}SsIvWt2#=UnWucs2_O7qXZ7r0d z|5l(aYi+zjZ9F15|J-vHC6ZgvRW=mPt1liOWg|RxAKTDbPks&WYKqhWop0r4Xk+tE zFTo6_r}Qg zm4Jf#?<%yU_JX>*N6y>FiLmV8e=|D{qQ}F{OXDaVPmFd9vAE-k1L`5H)LPe{1PWP5QS*-8Y{YK3P zJr?T>Rw)?9(SkU~&Xp)%VNsUdJ_nYm71Zk0oH_L;Ek z7>!AQmY6g>y=Xn995S+%#FEMDz^oNFHH4)gM7b%%FiV zJutHofo(>FaSQ;YGf0t4w-ws$xe2jpQeq#QHHFg}JF$az<{4Uozr3hp(%B#Ovk@3z8B5e8!-V&8<>Sex=0`hSI`ms;I!JJb zCT`HS*919jhGJ%4!qS0Aba|5Yuxaa)rxEd~f4c5XWxgpLD{zk9C=JdIS^TbB6YsJ3 zB`zr_VeyGDA?{Dh*vt6cnBjUQmCMf2TS2j4C4#H|@LX2C^264H#%L*zrG(tVXM`oa zc;%}D!`E_)63z0G&d&1p#(e%Cu)?xL(J*xmEW$+n?UA^0n22YNtU5z4_W?heO8-)4 zZPYRl+7B=U=ag=f& zhKa~pwGR_fj(xdm26nQp82SV4!5|Xp&=V7rW*o2jrmc*Q6cHWlAV8M0x3{YW`|tr7 zi!KH8=5yZKh+Eef;&U0~Rsrv7OUX3NsK}`;z8s(5gVq**O zS;np4ktRfjLg#UO7pjc2%o-6F7PoDcd-^D;aoy#mBxU^E!9+{yI z^++=F-pi(a3@krLXr;LId0;p6RTgHCED$r|G)dZAxhmL06*6-ByA%^OIba;Y;Oy_C zNkqQWGbbL*nB=>EZ@nC=d$#0HVX`dO0JbJ^;m%BU)!6GTI|nx&IPIduO|%lAsJ+vlJD@n5W8?DCh57KP>5{D7E_ z*LgL!3PiScPU}CHqZZ0od~#z$V`+382#F?2BTJQ_thpIuGLN)vt;_5?)12dHLf2g` zE}MDjnr!cD+nb?^~g{lF;HqW8u`Nt>8FmB!t zD@$LmFt`tM@Zh^*nkqmK1(IEFhceJfGh7+*%khqgw9i|H+9LQObZ9yI%Ou%h@KR(b#)%b1HTuR#Vdbp=D>jJ~UQ%E5nYt>04Jy}mgMnLj?5##U*w zE;tc8WX-(;n>lB+1M%xQfkaQR4PQ#jjxmtbzOT-aF5jVU`trU+)qwe(H|rub$h+o@ z)HF3J4_k_()@*9tIr9k{q5ix}Y_bD4i$aZ2dVd4?Mrg^zY@SNFcWn5V?m*T;#O~8` zc5T`{+4M*+*3WoCNdC#H_Q{HB8iM=yF}oO47ign z%~|5})L3l7bo!2yv4>K@?U&32Y8tJkTNUJtc)a!(9} zYnlXYG;uM_n>lOk59FGfTP<4@Ch=g4+9q$)4*Ax?4^v773&Qft0d@nbr*BhtCLKdM zU{g||y(LaB1#=FgJygDXdpV{v7o#sn%S*6H5#CG3c*aeRQqoBaS7WXuiM-SDymmjR z%Hi_mAs@w(w71onf^G~+Y5f!a7KmOqVIR)$ruI@duEU=_SH%3OAo+sy7c?hLlRWEo zGmubfW^YDtgNpk!0F<>x5-*^{vH-Jumsw(lQZ1Fq_r%V98OU<1Q1%A-+&S zTftdOYeHyo{o&vJ;G|>bc1$yfI7tsrJ)R0Pm{WMy;)n|c1G>~$pI z8}ju0=5ez%qcPcux8PjI10>B$p9)0C3yukaz$2{?8d;FahT%4#F;YeS89EO6w+z7m zm*p%#%nX`0MTWc_UwEG>qn-L`5LgwJ1dww4{+zt%TAyZ7bAmTi6HI{+&yW_9O@MV` zeb0`#c$Gy2dj5U4uje(8Xw777N?Hx;{~_P<)}$Bo-@a)3grk#ub?6_)t)H%5{16-n zXl#y#_%7lEX(XGGt9r}F4LRP8)Ch(Qo%OyhRD-HZRo8!ZMv9tzqzVMoom-nZ6M+M3 zqkco>IL*U9Pl%l0EN$SyKxE3%8P5cn2L7Gmgk*_9y?!K%#&|F2KA+7S);y(5D+e*#pOf^GeH2?5kdPk?*z`yk z9f-?D5K5#Lp@fd$|J;F$jt zpkN=pWhvM3e*zSIuT0{^v1#$A+H?-K1I%L55TtZX8uvSv*}1*K9cPAf--|2wd55J*bL{1hqhs`?qh^_WSVmolpR&7avICziq&Fmh=g>7J z@_8A}(Kc{C-Fd8XzO7}z#ZH>TjhXd}ULImc8OJ674*& z<999oNz{{HW6lF2X95Ua$34cskj;==Dh?kG-+a;PdOQH58hzDTBzWg}ip1eGtdF?yQvkLlW3_A1G?^}NjzpSq# zr;f83R9%lOehYdw8wRZ6l*LnD$Zi&XD|Xa-t!P|}+;&46zWhr$WX05F6Tt;1*D^GW zA(MYB>2_JW^1SAMo`%BRE9&CCVS2q1&y2tE46UJgixD%#P^6|XFEo8^M?Cu``4oUd z8l!#2w7zgO9sgIVtyd<27+ks_bI}0k!SNn3j32>Yp(BtldXKvTFp271+k~yff1;-| z%X8leFZE0dSdbe;+EX*=sbXExWW$41%0^T_^RB1XdB0a7BJUTU{@_wG)&73jG(EoN zV%?qzuRYWUB91zQGo*zhGQq{M#z(sCm ztF%*XYdtC$#wlG7lWDJ^F^Ab#s@P1AQ$jC}3(}eB6lW;q9jZ=69J{1fA17UVWIDCq7q^o7?spZHaViA?ydCuIAkE;! zPCPMu43dRQ2(xSIeJbH6}i8i1A+M zF7c3{IH4H9FFxj!L%v-Tm|uap!Z3uKDvian>Z_@jo}79eyK$V>isM9LzVI zTaM%Wmy@8qbw0*1e<}T+bLx#J?*0Vo$eo0ajm{+zx5c4r_YqkYS0ukJO|0TPdG6Hg zEKQ&6m~!pP%-AJ~;>jmc^1Av59wWQ|DHcIk1Z62G>58(eoD($-hqKgyv*wR6i2Z zB5ih!kdeSD`i?sh6N~&JXB0WtThC$e2tAZZ8+uGPdW<&W`ZK6j^?2L(@jeHq@^_^#SC z(2*WX!UF*3*AQ8>UN^~M1EC`OFmet*(YUAc`cSC>(t>*u24FKZG%|Fk8Mys48~{N8 zsXyY$VXUUvnmMouDl2L}Flu1!h69$**@YlE0ippu?Ph|7WI4oV7X~0R$|He{bxIfj z+!2Mtal{kL=eK$uW>8>JGdGpj%(x3kC-HFAa0@>IvaHj>hYCjs>1;NFfK3aMcv^O_ zKqEh2G3SH>brkr904Su4{p1Tct5VT4%SIT`V^#haTG44)m+&YfEIDJ}5-y(BwmQRb z0Rd#hRUQ}!nLgxEZ%QEh?}GsIX|9VXmfzcZH@)mH6xh!&hq51S05+^P)+V~gK>Q&B zXRXEqd>oNS;b)d}GE`YR7Q%$Lrqxy$s@NJS$VeFwH{dBfGDt>>QAg-eoz~R$zq?#X-MKxrragc8d!HN1sOT?H*@gsjQtgbJ~BX} zq>|5>=b%NiKD3dMuCUMq6}`kU)$k$AL(ZkyehZ)Zm3VM{`QUleOYfBC&gowwEgPEJ zC|}p?@%OD)s!GM?=Dn$%KpFCoRND;}Z%U>iwLVK*d_WH*w0{v9QEkIKjn|BS&*2$D z9^Dzd?UIh{lp3vQ=Po9T%Ymgd+E?~7#WikgrJoo1f!5h~1n?=bP+(-b-uX$(Q^m#% zIz#l6{uX+E`_#1J0NM9v=4roS&>rPsIQd#)*%@u48m~u>r|0ZVAGxL@&%-!%OUOKw z)Hg)(C z;0AulDN4ZA!};+9JP08A0YKGfS+88wzE6tYt~JmDDlS6lxX1e~~E;acs>>#U)Lb;Jis6G+Y7QhXMUXc~9qgp4WjM z9WQbQh&ihs`9Z5+hD(nES11iO0nsLRq{{DlFM581+V%9r;fzlL&8BTP{|Mzch&> zyQQZ5*I~M*f498(wwvEE#c(o@Ib12(05FI1M|Wos!29mEGvtPFmtI!?V4HCTnD`Xr zNNSL>6Nl8hBZ)6^TJg^w?K_iPNZ=6i&qI58k3Nkv+M;>LJJ$`KAdQbJ^$(nJ2FQaW z7@vWE6DO`Vq7A4U{<#9NHLTADPlDHMSNEfpE<)##LoXhgxf8Z>;>{t9buo(Jqj zN!qy0Kf)KI7py)T?aez>#-NR$5(M zz`+l2$#rtY?D{gu+3NX!p7X6dHu=J_Af2^3>?FJD(JP+VK!q4ywuUKZp3lymG%>lHpv9+FF6>nrrYs{(2|@$Hj`Pju9sGvA+* z@**c0A0e-PAm77hKf>oc#pk}p=Y7HF|A(ib0t-$A7ODgmoeM0s4J>)EToM{s`e-wa zV6VMEDo-TG34seuU?-_BTmBiPLSdVAgDSflTulgo8Mr}z9pWj0U4G{L8Vxlkg%m6t z{-kihfRprLB|P#Bayv1ReC1LqloE(4fG;hcI={P}Ws@d^h3HK{@9^_+P)6l{u}LzC zhW-X>nb1$GaP3+|WdfAj5R@PnGOQB9k1S#nA{`ws5>O>=TqQfKWmvgyyGN5_2qcYN zAZP*z-y{u=kt9#flkz}H;4xc2|2$MhD3Xot79KV%y$du6s&O%i|Duqp-U4Rg`sS3)%8zLN{ZM{! znASPxV0y+szYWz{@M$74h#qp?vt;!b8b^TO8n4;!_o+{CP6-rMT_+oABZ6pYUqX%9 zFN1>^;2^4-({Y&7pDs&nQU)?C2nPzqhUsyd{r-Yh+_VcIkX7ZnkkYF=}2*MkM!g@7b=^^;#Us%65L(#ikng`PaG zU5^*vKU@+j_H(j`s8VI64-9O|y+21Sk8;sdAyhsU+T7|WPOQS9j&C0QXNUo|7rboS z72pWPD}!gHtS*3vYVTJbP57vR^sH|p^$_e)XWVqh-6?07!pDASDD9$Vt(G6$^mN=4 zjE*$VkF>ZGX?Z`=>T#s?vq+mak+xqW?T#X`+))=#M%kZ^x^zCu!7j=+!OihlR_RJ< zOXYH!|FN=S;0U@ForV3Cw+!b9i@AeK zM(n3vB7=wd9wHc}E0eKV?w*MTO22o9x!jmHq zfSGn`hBhQGhU|(XaBq25ezKBal4I|XJyD#{usj7O3HKi2izmC?Pq8J#vLU}W+(Pea zB$Hyu8P2fng{a)@6f%S8OoE(Y+34tCj(wo52-0>bRb&4P8TXz6*_7X|W4?f)c7bn z8vGmse3tR|#bJs_SZelpBElRT%K*oiLypL}f6c9ZE^;G?w*Mvz2mA4W zG%*gw2Y}9S4~bL2f^;B{W5oGJ?|C153RRtR`H(~)DD;rXcaX9B`{_sfFQbt$atH-F zvAhPndlaj#y8rH2mfkXw@cW=Q1Brl=r2#mv5`I_QC{@V?KpKUe8cs0;yg0n!?LrB$!!_&?Yxgr1-?~b~;_v%jC8ZiB~!3humE5 z3IMg){vp56Xy0J}q^DEid8=D@#AsZX;xgS#EK@nYuueGJf2n;`NE5c&6R|OOH~9Cx z)l_*~uij1RkogBG2uK%B)ysma3gP4a6ieNEr%L4K)6Ai&15<@+BAvwW%#D-`Gm&%V Tah+T9bs)lj$F+ShfZqQBB~J*s literal 0 HcmV?d00001 diff --git a/docs/user/images/control-settings.png b/docs/user/images/control-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..d1113f035206fb8139d88fbf071fc1293c8ec51d GIT binary patch literal 246876 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
|l;kSswA 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 literal 0 HcmV?d00001 diff --git a/docs/user/images/controls-apply-button.png b/docs/user/images/controls-apply-button.png new file mode 100644 index 0000000000000000000000000000000000000000..da35ba85d7e2beba3239cc7413e749f99f1d0a5f GIT binary patch literal 383900 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/user/images/esql-field-stats.png b/docs/user/images/esql-field-stats.png new file mode 100644 index 0000000000000000000000000000000000000000..e4664a9c1ae2b276c4f674d9b1eeb3c732a2f41e GIT binary patch literal 132533 zcmZ6z1y~i`_XbKy3DVtg=x(Hy?(S|0>F(}0G)Rebm$a00cS}fjcisc|{qFzX=NUYB znAx*ut+iLY@7fb4FDr%!j|&e528JjhF02R!2K5^Z3}Oow0`v{zO~oPT1KdGT>@!&T z2>veUKf)&J5~eaTU^JlLVZp#d%)y{uP67Skf_}ijAislyL4rQPU%vYe@!wyeet(Dj z?{|o;mlI>R-sOUU34%!oe^Pb@KgfXbL|2*ZAqS9=q_7jY*EB830000Ocs!5apl#~RS(COT<8igL;(FA$opg(2 zLm_7^<#K;@2y;UMj!OJ`oZ$P3NU4#lYfScBqDI=GS8k8t7$-8Ot|)*#wvzbj{YawI zx?DUS%xk2G%CwmNVQ2WQ)s-B-+hMyeJw;NT;}&_&b*;Vh&@xWWXATYZgc#fhX{Nha z+!yNKK^%n~`peE}igJBauVG_*JDJw(>OB@?K`h?(*EldZAQUSsE zcajKxIxNAz~__;H%Mk@3ksTiAi29 z@|P0<I-I)3qxED|4$5W!T|j%&I83& z#D2(YxlH}vj0E9{ZTD4680oT{{y0+{}4}I>re~0t9hVQL}LGrTjQlN z{{R0=E-}O(L&|r2bk2$YQ}9Pr*K!$F&v>KJIm(x*o_qk07IhDywS3XV|2_PaB|!=- zy68BodovD2I={tw8=m84C;;}>FVrmxAqdav{*!D#zEmB|x3T98G$nyS_aMP0N8KWh zX%$JF)ICCKo6y%g69*xG{~%3*6utxiX-x_$;jAFj@otWqEMv`p;@`Ag6MW;M2Ts0R zmPWo_(=G&+kSXkykU&wG5K94Y{AYE(l(6zt?*63vFSGSC78FcQDCRNO(sH+tAZT{Q zFl!FR6FHa>qWm{1fCvH^I0fwAwyUIk5#}legglG6`VhdG4Yi`bvM()rHpfP|x2tO@ zIzmcc4Xl&w;#_L`IGKSfz-LApr`i}nI=PO$1^h$&n<_wha5DOz9$X`R$;eX|oMe6& zZ%=(UGYdK|cX4i+e(4&vN@dv?MnPRY?gv+_=jrkPr%?SMu<0BO$;g+b z?Z+l2xM8w~5q~t;SD031#-JC|VE6u+qB;=DbZC`mHEUT#zTJbCf`f&df^LmsW2bDc z;o&3eyLb6=4$mX5o&P2l0+weq;HG-;x)cD^;DrQg?-b*%X5H68<3`MqW|KTl-fs@z zqGPY-pFpZ4bYsz6D1Jew5v~m)9I|?Z#+LYJjr?SS;Jd6-R&aPtMuoIpl0 zqmdUX&MQCbj3|NPlE}fGI#N?#^hA>TBUZx2WaIJ+on@D{YJD%sa169c_U9q%?qI%z z*znP0J+J$fQ?$`qbBzL7`c3JY9!7`l3az$mi$qBRU3dQB&#O@&?{Jgio9vBrp;*fw z+`08)s>CX&wz%(#oSx&9U%#_Mq`m4~`4jPcaW4-+#PcTEyfcf+1Dg1^G@#i4Wg6Q>%XqPj?TMViGV#89hA#G< zQq0UFiR8oi$>(0}f^}Iq7vtO}4HGVl#&uu?pv0;3+YL9o(i0S|axB|FKi)0{g?cXK zshD_)3a`2!*uKa9Y_;&~unnz%)oet)$@nyB7?a1Dm-ThuO>k7$m)B zOw9({j=kw()6q=C%m^%oBpRJ+`Hp+;NWJ$!r;QT@1i+6G78A)-$eB_dB8Pp|GEH{> zfPff+dz;>99fk36kqAt;jaV8#aBzD@PMc-zX6MIlsbumn()k*bOC(s=wNq|}B`Yf{ zx`CeVo;AWO*B|9H9_KS z!EHAOrYm~9>RtQorx_w6aaf~qZ@mvS*&WSXZ!W`he!5|8Hy{5I78mR6F|oeMu+;3NoQGazr{{gwGj>fz z_H|I0s7O=87_dBMB!I`cy{)Ih8wviXHyQu)C5dQXhtf`T#$2ZlCU@m2&Rndk{aDx5 zORgRWzD!*CwKucvX*u-UealRX^zNF1!gzfCQ-jBGhfQl1zv0|!*H5a^1UU|7gm@K= z2ff5+P8oi=E`L||_b`%-u5zP;`K{>1r@FfoPF%k1X4CLR zyf0fcYHzmG01rQae|PaXs#EOf>wtguulkioc{Tu>N=<%Lwl4C_{iR=lTvnsjO2+eY z14M)HmRR}Lt`gO3EF|6p>(zTeN(2w2i%&*k3*j)i=QKXyzBhekX3mJHh z!e>AnW9_qm179@n95EJ)3fo5L-{YBz=Uib!&RKBC6;wox64gu0;Vf!-Vxnq(g7Zze znr%tx^&Uh6V$;RCjI~(WF?#I=bmx*JnNziC&)c&kDg}i8D7^YYrVF1(fjWWawEzQA zX4@b~EWgJaBNu4p7{W)nODL#J&ogGYOeVX%(YA+tcp4!#fF@MbKq76pPtVXZH6omV zsO71&z`f@~xWJS0JM)*g?P~_taIsJ5j~1Dn0@b;}pj?U8S#v$O@Q0$(s@o#r>$9ij z7jf*A#`A$e0@fhoUyio}f61OZI+5Ujf-C^Ayrk7x#rjK4d@OnQ3A?_>kA)l}Ucb0- z8=rT~KV=7#ig^jh@v2i6rDy&Q@~fuTs>`lZ^Kd$z`};1{DeM+FtobtlZP6o%$~s>3 z#`At@0}+p-^X4R=9*<8)K<7aZox^4s9>*MeKxhbtDB54zKRA`mAU%|p3$zUQ4>}bJ z9d5%_DD;wJbSlv2e|!a(GT6Y z^WkvbvEBX^HLO)9>8p%QNVr_Ef-L+a+otW7)a5}~^&!>yVRl6b^YbJogR(rLrsOB6 z7=rV0BPXInKsm6Y`W;HHYnykAiR4KttC<2Tcg627uNs4aQ%CohF!b+$gH|u?4dCo6 z<`p4kqkCvuFF3ITnRuc~#?#X-QHhdK|Jn5Z)4cMJ8=(QY08scfL1B{mSRfR$fGK&k zCXCg@L>IPoI33T6b9eh)tD5O-7Nc&L=tljIwU~dIUUt#eQ07xdWj(ixMTP3or(6TN z_lXDd&3Y=ej`Kv7%m}d409@4mz3GzHndXpi6f!n8tLZFDeII`3gbAQHhxr*>50inQ zoMdiOoljN91km>Lm|;6wPmz*~aBaBbbUPz?mKVSE@~?MPTnCTYl4m51UcIt?YG0NZ z1)kN1Yl80fR@&+#CCYVMFbx{!lZMlNtJisA2vZ>3mTK4HLQ||54i~uY@miz}hI41T zc0=MI2tm6(FE!~9sGP|?Ad?I0_pIteSuI*?4ZyDFpd5z<2Sy1@wmxafO(A*A+oyTY z2r#-F=UR#zKi(#n)rTH079?w`eiQI%by+6-ftCAOTx62!D_mxO6y}CcqHFxdlfO-~ zh)+R?AxMUU+TeIgL#Xuk^X+NyM6+79C(g}iR$DHQc|4f6)kzvEEZ3cz6@I%k@rZ8S z!~4o)qfs-=>Z>1d0izKticz9#{MjTTqK3#514(5^*=~j62Kd?81BQeY(Krcq9C>Ts z5k$6kv>L@@F^AI%s}1!b39-k1s1*N6wJL(x6{t;ufaa%JC_fP8*2R&wuf!gyW zCxSG0HwF=c<{G?)dXsX$Yj4JT=Z||QabLk4w#z;C704^>$;lR5Jg!Lg8_l$Eu0)Hq zcMTBimy2SbUrS~rl=g~7Rg1Wl;6qW0l+9c?YRiU&uR$Zmp9Q@u+CR7jKN%0l;bfM~ zen#}B00j<32yiirl{uGR%u}-|AwM_f`y0-;p*%iPWYyNk6{S=V=%!B<*UkB5Dm#}z zv;T6BfcZeES!25N^cbQas?EkmIe;u%L)@Q4PYZoPT&0Yn5LQ@lS;I(EfUQ=su#D!K zOiY-kY#n`5C6*Y7l0MEMy}jWA%QqMM*cnr^*%CAzz=B*XcC^rf)&H)Iv4bo7C?pC+ zL>r0dBgK>F99yj>#?Vu@;~#D(i;ggjBKtwa+WAIr@7=vwkG15+^Vy1(3#+uGvE?~8 z*bbEvW%sqyCiTNcgLPB%5YNQw+I2BT6k-E%sfC$vY<`glekdfMocitG13-^L5?R|JXJj)`2s0)`?l&mQ z1}(JbeF=!{?3;J1&iT?fh!5*X{;gh*?tvZ0ii?UI%uWlZ!w!y|!=Z zUn*!Wg%(GqlrPbixKLDBB6YPc3rA!{wt^J`TGqDO0T-?cqQ%XMxD$9%q$;#PqGKN> z__SCWB!uzsrjrKK`(2wwe0C-#`51H_rF>)^(P9D@;mFkWza34&I->5ou@S+-4%pD? zZThkN_OO)BwH+8lVvmtN+Y)_{!)bhwIE}`;;hZbj$oTj_4ZE}Y+M8t9VN}-kX37w8 z8n2EoxvGo?+v-FL=0agvO{;uHf9}>i>0XbvgI5entI_o-o5*F<$@sIUACh2f4WtT1 zo^Lbmu_&hR6(1=F3nl7B(z(?4u-~MD3I<|uPFVHZ%Mpc0OYh6(;1*r(;sIScF9|tf zqaI{OQWlnhF^q$L7`fo@!~rW&8{M>jtrHAmjm3%SNV##Y!gW}XZ7+mwyxoc}auZtR z5Ehc7u3C!3B;2J$X?6=UJ&Ceo;o+GO&AgFnp?pz|~rTK5U! z{3KPq?tcC&zsc(>Q5g(Aw07EjrmwPLW+b{4F-aG>(D+jEz7V5--_ve zypMTDJ?P9t1q0cB$L%4%qAo0FE5`dFXF<{F@eYv-$Nuk+yil8kpRw8K!a1(aM}N*o zp%)3uSln-IgU%|m)#NhxYp#xc^aY9tw8I)p>q?txY4V^OGx?41u(}K6vmp%UTDC^K z%tlG*2B&6oB$)t(ONx#!*b)d!!hc-;K5N9;)1*Y?p*&r@B7898z&PZ8=BOJ`93E^2 zXFQtp4haVD5c?)1sPkOVv)sLpp+23~BVTF?$Y3PZ`YzmT^j`I_uD{;rAe9t2Hn#E@ zr-;#cGw$V%ienlhHR7SttL1FT+67RV&|;bJ6fiX~kCuouTkjdB(rtT+zf%Z5!3H2Y z!zZd%A{Mm*QDt$o;+#eVDf%+TOzT z0TrKJLaViK^VE}7Y$2}$W;#00hgq}>@i`XUU`bo6U=qu;|Coev9!bX}6qEMx961^5KG_;XP1%cgK_@VlEKMI)u zdub~ABf=)Nm}H&#lo zSLE6bmTQoFh@np7DdjE?uY=1m^2+=k^?LCYhFT8Bih4JwS#6(shf)PvZWKAw6Ta*q ze5l%)sU=G{>WRKLn6aX33qa@2@Dvv)cEbn2T#7JYO3*Atc)4uM^|U^&+}t1g*xtkX;uG-Qu{xq| zOXKEJQe z2`wAM0EjLfqph;y$=LB~5wySOBh8)lUl+r0iBnEfpu6Mic*qo3Jz*tH~rR zr)>)*Jecr%_owYE>zgB>(x8i?P%rBKgC0UFwN5x0j>qKzg^5m+p*NjubK%3n2OOA_6|bbMhcasq_QphDB*`sjGRgfTN8H?XUp6uHNp>3idRY zr2dT<7ylE93%pp4GH^&H@=2j9!2RqLda`qVN2|l#7AT5%Uu_t)T%FhU`9zyDc4j-AXlxNz$|F&*od`Tty_!9}ma{2$?ptkTVYIE4kY35G*7_(sGB$D7_y{V7R?(s8U}p;>iGE9BKr7dyueB zU41!S`OU0m(o_mRxOle~?G!OykTh{Y*g}^!GU6>TR|Hu;oGiCvxjuJAD-0{~0r|X+ zqPS>;mc?kpooht!hf8PumCQt%grIZ*?*~WqFRpc^8t%@=^0oGED~`Tppc+h_^1HXI zCiRbHEceQeU4Z!)DLEQEXqwr0!$qe(132iN4*trehqU?foaC{_-)=`4PiA=)*g)ld z06}*am(9J$a%cCE*4hr$Tsct)KB0gei;_4jbS)E?DQ8bP8moaNp9Px0TrP~(CAMMZ z$jQU)`x^(cy21{^N@pxI+;Bijm*cR*Hp419Vrex{|KYv?1tX~cCtQ*fI@ov+cg~L= zwg{$~yhz-N+H(X2merrIr)`j;zdhV)~ zL5nx5+cgA(namEVoyby&B?xLgzTuntHxSqp4|Opv5OKLcZ)nRcv8SPGwVOz|+h?@X z))-3CZYF`HoU|7|G0G&J_NSPNN7NrzaoVb&(nM{hRD*{9z{-yY^T6w=n-dY2H>?x& zQj@trIZl1G;J&*ji|{>$qqAaAqqsle7v6xt&bG*|FXBH#QThzK2_-Adt@H!1Bgt{O zf%>2J!GXOunlYSW@I=R#V}D5bK#+VWff`q#X_7y?doOmYBlXXD3aVp51ifAx<&oIT< zT{h{E6M8wI6YeM`x>jKFy=`SUr9q3i$`9Px)T(I;k`Eh$G;F}Bp!e`Ekyp5kWEFz_ zSoiEZzQG zql5o&%O@${C5j<%=$K69Iu`i?$oov+pCyDU)F#+X+kF+9j;Q*D;Sw*QQk(#a_r^hP zm(wAmi3Q#Iq>!n(j)oLkl7#=$;)DE!;+FQQdYGN^Ou&ACdY>Ou~>?sIi1^L2*udA&W|pP&nY?A8b2QUqbbOZE0tepxT=Qf=-1*& zX)G*kq<2%@`|8puoMNJKh~mq?Fg%VV0d*eKDLhIu$a2L>tVR2?WZqAk^*$U{zpHTK zB2wd1{@yN>@Kpjw@v|&=z3vb@#%SxBims>7xJbM6YIBO9A#4IcKuHv`eACGI<2OhI z!bD~0V#PW#NSo`3dz-dK0BQvwn}jh)w7aXvz_n&9p#1PpJT?PY@#$WznSR?wVRj6Ln;?mrE`W59L(uKZ;gy>!tvNTy9~vvACnrg9!}{HXcuUa;yL6Kyv{(+@B+%7Z<1{0ImtT(*5zlVW>;D}xDl^Va6`|t?=^+K;4njw;HW6ScIdu^St0h|gjZ4FqF6u2_#PuSR{l zd_!7Lf?q6y4T?*Wrj1PKT1dzqe*(q!?&Yb8>5&`U&MCGU)?`K2sq@Ub<@3;~R3zOT zJKf3&cgcAaSWvxw4-LZ*a)+dn4BAAJwj@7Gn)&V`e|fefTMrCi`k;N>H}0&A%uThR`|{P;HfyIyfj1 z0hj*4bVL9&S~9OOk$*A~gqVS&N;Rtw{Wr{s6zVJj4vRk9gmuJgc?7Zq0tj`IFlLGE ziE-e2t|nqf<20PT*ez`vk6#|2bLu@tUmx)W|KXK5<*NI2|67AW5%V;5&>A=1n5Kh3U@dmRb@b)wk52riRWQ7jOb7e}Lh4RA=a zX*GWGo#T5|CwK|ekc~+R`EiWWWr`1^JR$bLRm5}W`J)q)$eS^V!u3ggY9SNsu)<4+ zRz$5x56(TKEc=9$`txC)`_+jt%|HrD+x>E{V}JuY?x%O{loa&a!{y>Ygn0%O*2jUd z0^%V>K$o1L&%wN1at+7j(}-(-nOlt*44~Il(YUtpC4XCXidf+1cwM)TMQULz>Am}n z*68CA&0m#7o1&uMy`2%4=_^$s$ERMu$uUH@#Mrn8FT^hqT*Xg_Bjt>1}hh$gw18<a~KLGLfHg_jH9cF;ac2`UZ3qUGP zk%|9&qzOv}_!^dgg1D65+o;juI~Ro?WYdfF{evwSI>eEn@PZmMFS2w|fbY)O{kddT~qapOVkvHKBHYw41%*;N!2Ltlq`RHB1gObn`N-tp%$K8jYKwVkht7ARVt69?Lm?6|Ckg6_`Z!2W$YH+DOH;_wiTd=M8{Ph z;kA9|rx=(GyywOJ3?GdYj(cu3!6x@viGM3I6`5CO>Ps36I$V8y&s_>~v^Uj)9Yx{> zisGOlJ5KswIM{Px2mvLpPM=Lku!VmkI#!yXUKS^nJCp3#|7CGbaD#V7%mb%(#glHs zu~j5F^-BQkxw3H_Z-OC%I6IQBK!Tf$AHWnMM=4s|@{iX1sWm2HOIkxkh6V@hep^zW z!tD>5wG)*zc?ivU%H`JWGx1}<4jk46dfERPMW`V5ma=)74!AGDBAA0oB@%NZrD@Kr zlHXi}5fs9)sgRRl>>agV#251I*ZLMnC++2zVi#J9^R%}s@P~a?W*M@RIHNN$ut+2 z8?4;*O2L4)9#rf2%`c>>lkoV2e25gYO4|uzt()j;M-6Py1!tvzdU_UI8v4fAW0-s zd;pawOBKA%od}JD(u)6QA)R1?kGncsiX}w~hHD2p9xRuUi4v%wFjL4>Q4bX<`2hQc zk>hoE+5+MKcJ*3?X5mgSIB>tsEFGDtPh5C!Pr+)5y6&tUSxaY~;2O~?ybrz2+i7Fg zh}W>d8wUpukLODtkMDHFXo*NT`WY;{>gQLdzG?*VD*K1268Cttrwr% zvq%cd9L%Wtub6-(5M4c7#`3n6jUw)EenY(dqaTzBARLb^mjTyFi!|?}Slq-I4&M-P zvm;l|#Ud!U%Nph;JVm+wZ(jvOTz1voD7&5ytKIOzWq+m30qTp@=b}JJ3VGjOn%(J9 z+TH(v!+;@2Auj<{zLw9LnvM3|=98u}saFB;nBZZw?75%CK$P4|PEZ3iCDmIqeb>N| zH6Tv3f|vH*M7a1MFFBpAevK% zg1A`FVg|7RJIA<1G7^#|fL4r^sntnH0j6Z9HNc`*$C%A%tX|A%Xp~dj{S^_f?W(?U zSJcD1v*~gjvK$Tr^Z!q42EwpTgRR|cIiD@45_`NzWi61+?7`|sqm(VzZp0NWnjn9V zK?f$pf8$aKYBM1Vi3tu4!k|7Mw zq2QZWZ9f`RnGy2l^DbS>`JF&wNnFK9YuHs(-L>#Nao(~$?bQ`)5AEFPB*n{zBla;-Y6POK{)8${L5 zr)znGkT4&Xd{%)Lu>YqUV>2-S@v6C>)$~cuO&^^KX!HAe-F|zZ=!NWi5k5AoJ{2nA zL6K3)parZ$W|t))rG)d|A3`Oq0@vZ);Gzoeb&(*PfIvW7|C! zGOP%IC}TlzI$kga95WyO#*uW|)7RJM{kXzwzmbA$^=GaQZ4}hRQTKJRh&3uALaV`+ znitgDf(Uz3p_oU?MzZI(-2vm!*@Kl6tGLkK=WysNboKKA2Ktk`vD z61{W2+%w8Ls97(Z67lV<6|}Jx`Bs~yJWMZc++D>{UP1(~xqO+34#@jHak3tSX2-*z zk9l7AZOmp@jziY;YISVrOaF&EhvacS5Y+(Saa#E9gBq~b@KA8z`qk>K4?jWP7m8yO zd2+$81F`GV*$lM9P`d$^r)!&?{P?xjOTnA8s(ee8gz20%OPbY=?;^1>ead~>mQkH= zPR6nH_Mpp}y&s{c=wx-@B#i)7*~ZC>9nN+`ZhlQ*WxFRar%s|#b4NA>batC~Yph#E zgmAqt%obm|64hERWJT;3kHn`zgiA=_;&$JtEMM{vk~muNL55LT|5RtS?D%JPg;A^3 zVzr$g)E4#GbF+AwDIAmDpFDca&Jht2vGYTD-=pUNNC~l46C1nq&(>NEU#Kr~`^4$W zy40zv_huR?u@sE=5^Tnq9j6u0UaLvLZ=~P%`JC2C$y6rDg;WX=(0z^%Nkj-~))e^= z{`oZyKbiYcugAp<3IED*?I;#EyE0u0ImE?T(}IB*3>9!?Ai78ff(jc0h?A{!$`d6CCiG-(kpQlfi}mJ1V*(UdEhI+KnxBEuHnAA z9I4tuqY!tKL&BaL9rT(406zMiwjJKw)f2dL3bTc${OljR`L$d|(=sz~#p7vzU)ZKx zd@3r06t4}1d$%q+QNW@&5+cl4rB~8A8%@@RcfFn~9{W3Js1X}15Cre!8gCA!!AxpY zS$;6h3XlBr%cKMWk0UZG(xUv+b*N<5iRr$X-qd4dg)U-Lo37B;l(X=UKlyZzSPa&b zhkU&bFF(!A7L4T7T1>e@1;nSa1Yr-bylG!Q*4N!({}j3)GcXuo2>1yp|E;SS7zkwy zr5MF`Wvw#Ky9XN^M)=umfO9@v!jT1|Pekk)`$g7Hzd4_XJ<^UhhhhoJvOZekktfTY zn~n;K8vb2Nvq6CU@IkX`P41>Dtt4?tNiK_@EKz@cBV$kc84juK0|uPSwKc){_9%~6 zPfTw#LGOZ8q{>jf5n~xFm>i?(sjd#nm4#{yK0j zkUjKpK9)u-3i$ClX$dJo#LwJn!7LDAC0&CJCTI9bP;$46dALbzfOt$-L(S`=NJmHh zTrwX+>mlu{J@!S8kHMp->y&?~RjM1#1fJ9hs;Wk1GPr9=4uMu6E)m|=5S`I_Q3-}D zJu3>wgcE^P?b0bmCesafCB8uRc)7)8y@x&`nXXrzoFgWK%OL&sK(ZO*ZCw?m`0`t& z^ez6<6Iw2kZka;U_!#l#E&-XoUp`0jEfDzq)Dp-g!<2BwZQ)I&m2lJzCf~HU)>TZO zEMS=!zXe7=Wac$GY+KJ;sHj9Zusa{mhZyfR_d2zIfa`}HZ}>7^Z9=Phd%yDcTzyGQ zg_<(>pu=HnsPRvu1MZ9gH+2vsZkvlWPZ|Qli@9 zo9pje&oU!1peUh`xu?0!94WN?uYehw2=J^3B{;Vd(PSB%@z>P({|f6|y7^`tDCJ|FyZo6M*;eteYN z*k(vT#+xUZIQA&-C=P0Bxhj}a)A#50Y_0@F5knKxGR^7OUyi5M_7iW_ClfL<^%vWwQs0+;e9rZaxijm z+@FT=tD0mYW=?Ejy=$vd?^V;$h}-DGU~aw(U*GF*e0_()nVO$)ZV9 z5>zQ1qH=j%MpEa)1@^9X{t~9(76J4Ue1i+u#`Eq7EUbstjS{<+zY%LJt6`-O@6Imw z=4B-LPF)G`IsM6FvGcmrkOJC9a@ffu)!t$yE4Y=y%zdq2JjH0c-s6S#^>PK(y{ z$oKqMuP3P?6e0ZvQ^0~N#4H|-9NR)he6k%$zim6FkdA7y>x?Q^2cf1fC>}GEapXAJ zZ#fz}{x7L1<<+IK?FBip(IX*_8?!unbx~jKIlfjBEchW&RJ_n_f600cjf~E>2WeT= z$Omnsno!6yMddh|jEjtK?M=K>Sy|aYkY5ktBNy_|x&6eWr6#A}0}?}Dx-}aeoGd(sMN(Xm1YC&!YwTH;3^v>haebepl?ek)j)1qGh%=OVyK|08ou&@5Y0tOT5C(92V29Q7< zagI;^LCr45AKlK-5FGsJYQ7aEkEsQqo%3`;~^c-g9n!qJXGHvUCbkoQcCkcrE7qqvbz7y74FM*S5 z)Jl?jX28%I6d`m)w0QNFZz zUNq{-cwtJS)^a{c#26MKoy#tK0I>zr6d1eWdH>iWbFCV9wST^F@KL~K`JsXa0ut|5 zoE#BvX4Yim;aywX!eWEfnUBCQ4%bf9Xpi@q&*2eTIi6h)ikFtmrb-QvJ=u(r6s}HA~(5 zE!ZBQ%F_bca0F(o!P0e_er-@mJv_o71dV5RzNe8+Fzx{&Z%PFB?0em1@p+7lF&DOO zFSJ%#^&ex=|wb0`i8tmM0#b0C*kW408Zj`D_tX4Gp!4zJQ# zG)s&O{M}`x(kBI{^*joVfdq0RNe!PRwgwWpxwC)iqs(&UwVWxXRV!08OVWpRJdHC+i7m1C{u(@s0tEL6 zfuJ((;!yy$mS)0mQb9o>r8~Suai8xJYS~|LU|N!{uAAxAh58Aj0)t}XDP$@xzdYKs zK9x$EELtC{@-NmMr*n8=I-4C%?Y>JLv7mZK4ku@?8pr412x{hZk(VF)(JjTyRdm;8 zv+@M$)>SQ1#G%Dl@j8aw)T_C&AtzI-(9L;Z+MD)3oi$gYSX+49)RjtN2=J4uG8CD8 zs#UaVG+Zc_{A@7t?W4?I*WFNbMiYU6-Di7BnanGhUy)M6)UqOsTkO_FAE8GJ zZ!mmHb0d}Y&4HkdUSGE>)JrwvAXcgF9D$XHu57B8)x<6LV#jVIzCT0A$K5zEVoJ0wcz{7uHun-O@Bb1|%70|?S}0va;vFlRjL6_wND^L%Y9mDP zE0uQPEUUtcSC*xt_B>vy&vk;W0p*f0H(!444H3~k2SyI1aoQX%)IK%xoP_c@=^jp_ zF*g+D4JMVWf*RMq+IiR_&=c~xOJh7d)W{_=&@247DOBjxvs#g`uX42?D#($iho%9w z?@tztc6D{>$3sXPJlf4T0$9yDo>|NWlT<*4@doriz{!hJJeze9zJ&=cGGe1R`M9&g zg=vmhmHe8$ZYGG2CVNPW^+sCX787aI4*s0JJIL7*7n1x$C4t>9FX4PJrrTD#{ajJT zKo*0~N7IPCro@68LDKpAcV+zE(<979Y6-m-$83qc;qgL?*l2eOr6_;m_TA7XrHWh-2fs7LZ zO4oAExLAhB(xXY^|HQb+4=e?TfRNSa$4bbz@DWB1MjpmE^6VRz&SFAQ98ViaYSjrB z56X>?CyH7ez>B!iy&t(rS$Vj6KL_2;QE6U~v~+`Znd}eZGN!i?H%8&H&G-UGI#LEKw#ErJD!8N;nk>J5oAfJhk+KW@{iw^^za%FWR(>B@qIV4ur# z@F>j+^ozNy%LbsNmnHzVf^hFDxLkA+6`jLOUOQh$njC`h7RPj+Pi1zP(eo+GU@`feI+54aPYVK@Z~?0w zr$TgPQ0;prX8^QF*QMM)r=r^kQO6|P<3sM{ zmps5c#5XzGtu)rk*qFvUAtAQt=I`0eo42=uI!V@7h#MHokj36Tm5&RRrjWG14HwuD zM%;Z^*`L{Wi3PzbD%IXC?gKr66VyrtB>-cCTCw6{V}H>n&)W-Z{H1Iu4ZcAuBXU5i z*F7&X7M9D!PSx#uOz*4PWE!i@0Rb{HGWw&79Lez)K<^#YMv7RZsQaF-ak5$_;VyNi z?B1zETrb^Qo`@<=17ST1Vc$%Agl=*f(*a42<9?{A2tK};!TmLOVVMGyPE75{U21z7 z;gt$>`Q{ExW3d?;5uV%r$kOA+Kxo0TbFrpLcRI=pAP3L#IZX_8kQljl^4_gR642c4 z&mY#T^(m^s-|`cp!{amur>w^1U4p9lI!-zx{Q-XrtLCeU5Yz8Kj!-^f%atV2dp~~lJ3?u8tI0PtdxE%iGQ0nf z0m@tgF%oMnX4)kOw}#W6p6*HEZcL6U)GBl=pV!V)>h6x)7QfT{73&>KB@KFy)u#v6 zul>;hw1Z4*E!zWxYF_+4KLlZNwQhrL7RB`T*-k`LqVnXKpd{Rn?K9YYwe_+OVNPgD z*tZk~VFdF5`P`)_-APz!y=4lMTt@R6`ZpyoqS}^25^Vlv^kR80)$E0c_+H4k?qL3K z>uw0HgTrBq&cWgM*Q4po{j4qjl{&!=TW5HeVRv}3649mPKq7q(C8y03mR_5qH!$|Z zhwETT;90w@ShMj=&hyf>3I<_^meLK>f@;&2_8o%wQzE>IEH)@{SCISkie8J8L6w#Q zrEMdtCci9(HNXYuq2%&6$jDk;f*b#1qgN#$Fv&x5n3gIQXPRNj-46Fu_D>080#$!% zQHs6?f#%b|L=qur1aaK-!v%h`6Y*-3h%F=m?G9j&mt9u?bLYIkFv~%$5v;f64sxNG zT@tktcSnl@KlYXNDB%)fA`=J{0p`Q%Bcq-nb%v3?J6vwgIR}udn~Q%CS;*T)q%_Ip zEUbP*^ZhnH3kLp(ix?0$el8CRzEhMig+-X+c{r#kGaE^XW7N~#4E%}5>^V2P-pZ8q z&Oaq7pFksx)AGl_9yHrFXo+$=eyQLAZ8nr6r&TL%*o!go()F39qex@7thVj=wOrBq z!{1=4;(6-Zw2$N7P25pn)S5bBqvNSRs1R`7%f|ku5Auw}?@)Xn(8x7FF*e!x56Wp6 zZMc%q-C5Y82L9S;D9yru?*bUyn#X+2kig)$NLP)Mib$l$LA1rR3MT?b+}S6yqi=A} zoR&#+^9~!fQ$9pD721vNXH(Rr97FNj9T&TkJQk-@MU(870{QkN@380}WXKP3L9B$? zNM;M6H=kJa0d^ppO%ECxng{=r!}$z7?1HD;xnXSXQgK(<&0sR)U;Q|(dc$xm#$byK zDAowj#J{4p6Mn329sfb$$i8f*JetWDApk5w@(!fY;v6GtmN+db(Gha_JtI)4B5gldlpKs_A`%?eqp}aR7R707Z&B=@87>%;$%ws zLy*vj8V$Bp;Sa7;p~<@L3gqUmbiozuv|nFaJIXuM2&LBiNmbr|5|D#CeHvHnQKHH~t5cjSIq~9q{EVf* ze%MVuopXJF;!`$?nb5L2`u0bZ*eTNdljvDH(qq^^PDmUFs`3Q#+elvPw}`pR9j6~* zP--a+3y3Y=6$_vSa(2r;IetP3#9ftx(sU3b697ew9$%w+v6_+JRZK;uw5DW~^#&&& zk?CUwUYl6LiN%s)#g|b`9kmzZ;NxFx;RVDH@;Q_3iC$UFgQ6PjmE~~S_3~D^Dy<>@ zKdVcDZFrc~Rzm#Wet9f7t~h7twoG&_H|2>&KkaM&erLrJtStDFH^kRg+BaU4pvK_C zzX3p7$Hmq`u4W5tlNqlV9bscnV&R{D1RAf@lz5S?(*rR$Efnzs0vS+I^0s@dqF56H@};lfAIt#KfGGrML+p4J^LA%Nkf=yg zF`Kbs<(l_sw@R-E-u|#$CfZm0u9dMgN3H(5J9dCgvtm_F})t z5tQ8`NRAF1E`oY3!wr?!Q-gC8Rk8n5xOtTCy(Jm*2ROvt<79rlt%LahAqbsWpg!V! zm_Jptq&x=bT?6KaI49n3R*otm_%o}x3mB-!+oV^Yfv&9a-}ZH1D0Fa66Tpm+Auf$Y zKSA6(Zh4jMIzP||;W?WB4|Q)DRM*mV3nxGz5D4xTB*9&S1b26LcXtR7oIvp4?(Xg` z!QI{6UA{#~&hxzI{qB$Z^Hx!*Re`dUB7Yegqae@sbE)?RyTI?9oFG?{)S*(J+(fC` z9JEQH*vVS?xl1gOe6j|}G3#cN=`lF~O)}U1_Q=yH`RheyVAfu}lWwY) zVm<9VDrD3DLRSGY2a%SKvV3J8np%!nzGg*SW)0P*>) z%|II8uh|Nt6T^czp$Wgpi9-O_1VG-OR2qxVq*Nt#0}Ehmf$!a)-_0FcFIxe6&SGb#gisDcPnN&VWzNGiWBnx)A-e~EFoOizLb1S`03GKxM#Xv0Xn7OK)v$^?^dpk$MONcFw)mC#~ydpMb!CDK}s-yaHSC@1NMWjk~d z8AUEAI%T^#m;JW(_R^6-Uzm6~M07fRteX)RV|h}rmHp3WWd=i4jw@8)6ijzpPILy3 z-q*;#Hir`Hm#c5yWrhRN=#_VOiPhj_sg5V38=jzkha8%Sy}M&YW_wBOb^3z|LQW}0 z%@5|ML9tIVIA{Qv7=QdwGhb`HI|z0FAe0aLXD7~RhpS@}{Iae!4Trqlx0jgK*T+%| zkaCRL*Y$Oq_kbwW{%CP->p}sV)*9%j#>7CW3iAuE&so64eQ}%owiKOX{CfA{YSDGK zx4YvEP@GBr(jK~T!f7gTPh#ik?RoJNK+9^#!_b7r8LtN5-Jk|Ggd0pwAMKcOKv$Rl25UordmtgrKJSCG7_-JCN_ z5&w_tZ5czs?FxM{%wLW=y1Im5yQ1Vv^KCvND@GG#Mw}iT zzOyNiQm|~WUt%yBo6y1{@R`hcNnn(+YM+3I(Zl2VoOolv%EP zJD9C%ZNaz)C=}RVR@Z83=A1KZTmTh$8s13kry(|qYTx?p$5RtbCUYr0tMcs_LqXdn zsr1{>xSj_wv$Se><>ua}wRIkWS<@yMb{_&UZY7WC}&oDUT7rtSl&dQFq&IWZ_3? zMJFZY&)IwJyb%0=0DnAbxSqgax(kmR+X)4Vv(UAiv0sCTyV3j38_K=|ovkDU+EUA~ zNMuSK?c?6uQ}E~VY#n=(+Oo{lZSe%=N}$f4$di*YJDM(4;T2!whgo*bvf=^!RS58I z0EH#Hp(ZXejAg{7;@}M=CJ4+<{?>_4;bz1DqG=4U{qsY|ij*qpE z56&WJ^o!rA?7Sw`n!kb?qhz1$0Zry5CkJNjl;9=Tbaze752o|7Q-_UKiC@mT;~30# zk&+#j?gYUFiNvs|(j!BE{Q6n!>OS4WAzwr(onbkIqeeJ7LB{;d9^VqDy1ALzbbhF} zb17E1YmZ^F;5t&!@EuvxQyCz8yhcPzerh)GdBt|^?Wb|Xu`K4_4g{#6yHgqT!Z!&+ z98b=N4L2HJFAmm1rli^%qzPRv3&yfsb1-RI)5%-cjL zP~sc?lOq5;=N6UpFdKQwpT#9!9=y`kdb*>Sx-^Q}^n|Uh{t}pmmM3~4IHu5yRX1h{b+j}^Mg5p3)CToiaLo8h|7@=IY)#H(v&Hjb z$C9;QZG68pt4+0JVg@;+VP&yCHDTZba4s_&P@coNFS*Gnmnh)U)9BRVOB&)_jzcS!Q^LkVt^duujcG|=(zq!({V zIdqjEP}f|<%sh94_yd&#G(No77GlO`Q!QC|dSD2F@Mj8kbNNiF8J2i94`^lW4U+J5 zJp<89uvz8WQ&>T#6S*?)k^no0z7nl=pbNT*VH=Xe3vE&L^^$J^_HK7#_FI*q*KZaM_?H+|89q}yz+5Bubz zFp+c(Ae5s-ev_og5j++nVsrXRRE9%M1ZOEgvOB2-I(J@V6@c%XaEl5p0`s|>lHJ@~ z#83%eu^f?h4a>B^$w#H1Qz%k}H`o-tV)S6}YbB^(SnoYc z`HU)~LgR*nEzi0dqm$rrJQsfUil|l~^c+2^PF3T2br8mTo9}m7r++j zAL19V`)9l8P4L*#BScE~?`rHmOz`#`u(QSF)h!qmF|?dcsoh-bmS|JT)3u7axYns^ zR2Zc@YJfzqo)}PTd!Lolr0@G`Q%fe72L{WXbZP~l$#xXw{foO` z;{(tQeD`_705z)CPS$5mr@(Glg=du9Dbng}Q!LD~Gmt$afx3T=&yih%K)DWD35G5nh4NCw}=J(d-aOS6`9X69*Z zJ_e>zPd_sr$o$fH5Kj3ADLT}%Hfc%$vV*>L5{Ek%mA{oBAq|^u!eG z(Q-6EIYOyK^R=UV(JdJ%sq&jOa;7PTGsnT`2vK>LNGQeg3|aO7^W2mBN)w7?~en5Gfo{tjE2KSL#NBap0blP^-0 z`mx=ylj#2R5aMr4K86LzjhB^6G-*tk=5%PDhe)&~pu#3x4;zhC3O5pA&e@9prS}uc zY5Z~dMyQZmc(bxE7pLw3^apdl4}2YOe~hIaWVX~>8MaJO7fR{2cX1Fbk*rBuP$!h8}`_xr?uF|9Sja@|D<<& zgKPOG{ z@m|z&(M(f-4o&2Gm5JpMpe4jC)$sL!$UTMzJ~0waHB$$hwc!JHgkS};qvR4moD}zI z^$2`CoTEyq>kM!tN5SSW7d=$1U50!scr5{KES9TIfs)m9HwafjJO~N_8{pYnq4D)3 zApAR!EN|yG*16xFSY{qarT!62V8OS*vI{6Jo)HP9FVVp7EygnY&_7cy?nf&SNhh!_~YP8n#(^oB$QhwcA^5fX_}@(8CTH6m4Oy=rsx zieL1j*`51U7lNEkU8&ZCFxFHmo1(qrOe0-J3g6Zk5 z05(vvd2HgH!ko={#+2Bxo12Zk*!JT)l*ofx8`QM4v@*S4f#pI8w*3cLN}&LdD+W(} z#7t3Ld7~-&0s|a+zH4|)Ks4)+sW3?fEM#0y zOcbTEYVF~U=MpvQ60xo7nlm#}zQ-Jz?~;PAv9Y-Xh1H}qnY)G63?rnqn^WI+`y%Tf zvTtmo;kyUv-$*;#RPci|hTaLhti&1?kqSsAI7}wVuj6#6)nss?=^^|fPRj(txHnbL z=~}a3GJz~Ppt#jYT>Hbg`S(r1b1sFd%~w}!#)6ZQSOlTIJOa?Ke^Y%tsv(z=$&_ir zV4U)jcLBM-IS_V36^_qlnOAdprmP zerR!8JlqEFJq*jN5yaM274uU^B$b<}gzQb$L zSe4FvE}9oNRu3@MkIS1G^7IjSjqa@lpNIT3B4!HHK-J9DJ9Q{cCR2ijLv!Rm z8~rof|6C920e=*g zBtJa*c^Mp3uD%ztIZMwx&-4V1VpWHO1%pGPKj>6$0Ot?U7&x1wb{m2N4%HqGQqDWW z?U$A?ppiS4M@^0CzwJEyb03_-_$}xeBs)f>#V_Gv@zb*JAh`x@bG1w7AK@fm%xel5 z^E&UTD?H4TbxHrJQhrC5musU40MnjzJ^YKRVZIJxNBA9^pFak0upuT;``G39vJn6C zuizN)i?Q>yt${z8%?tMYfAbPW!WOy6f4&Vs$C?r(`c5MD>IDc5B?d{u%Oym76Xx@R ztxCfKrogi2)J|moeu!l^kjSt_gMO4h*fzkYd-?f*3=g~~A8bVR-*obymo5@H+rKaRfAnLPh0vYuF3l8J_xa%!;x*a+9V$XSARw=a$|$#6O13el((uja>(CKV zF}Kpf&qbmKm#EwC1(O0wkd@|6*Y9XP1ZBN=$fj z2l%oxeN*VBRH?9;Z#<`ZeJG({q|~hIiT=T|{~2yV&1Zz3f^>5-wuEfTf@?>}?(ZwKQ$*nDJIj28w9asZ>XpRVWo zis_gLYT0jja301|wGo2P^yYs(G*IV0qvYeQW&Hhm7yiDHcLH)ba1&|PO#U5RU6;7b zNJ~H&RVS6e_wTv)tl+m4;MaDL2W)z0OL0bpF(-90xO3;7NdkC_Cky91jwVc|R4Mid zzaA{@We;6HbLLX=G5Y?sgD&B=riGkp;Za`Q);6B&n|yOjP7xpJT0FAc z@cc*BvkNA~sRFfRS!~Vv{|wd3A_qEJHRT0TiHC+(8k1Ux>Ol0MoWf1&Q>&*{UV zkkO-N{EgTD`QR3&=k2QK5*h=6Z+6n+TfTX{a>#{aLBxFZ)3PTh1Ob8hI#p<~{Q(BL zmzUhh0js@QlkI1PL)9o!c}Z~^K&n<_GKDkEU?R`qw%MN>k$u8<+7S=|fOfu8dA7aW zZ{D?p!s?G6&R1^rtfT|A)|+=I#4=ftNiIx%mm1@)+rwe)0zbeZAW%tVQ}{0g0B_o2 z2*nDCBsQPNbbwXx*O3hbEx!Unrd@^#17ovT$8}APNN-CEB=I9{Z&dnP{fkT7Z?H@T3Po0 zv~FGoMw$|EOcP{?u5>L8uRCDZ1^UF(h6e4v19<_@mbLfWjRGc9NU*;#>y%s+s{78A z4gDuJi_QLIxMuB{dmsh6XLL_Rnkm!6Y${oDGATg-n%ZhO%$D1apbnq*gmwSpVk@)? zC3bh<5Mj`NpM2UvEcs!{`>1no!cu4&(^f@^4f1u9+fnNPC7SjUGm4FEp;&TJO)!uB5Qw;Cw zuja}@CB)WTBJt8|G!$l+GxEUn!vc_tnp(V57Ra?8D`Kgzyu zvJ5zr$J$V~z-Hh)4D7zca!*SZ04hiZO;t$Qt*kn0|D zd9>qgMsT8&VAzx6H@78X@hDypmtkT2LFo27v+rcc?k?SR2KRSj7LNwax$a@_Se=_oq$`S?vptz5f;M-cDMu{Sp5-kJhKF{W?(-ii9 z+*!8)(Vw$}v+r_ejEwrjt4qLi zO#O%zjVUdf&w@xa0cr^FM13&qaN**rTCiQhVY!Y{xI0*=cfUE61DpfXyH!dHtAETs zJAG(u4*Hz{o~5rOcbBS6DE zsX?;-3+D}=f5v%D;m7~ty!~GP#(80XVrxSv=zjf=@u@H8v+=3aZ^74oo!wzCA5tvK z#+=W)O~vt0;>Rfs_qD)pF`cAWmNiK@U6DCHVUOOc)i}c_%_?eFy|Bn znv)4QfQH?@GE@EJaecP=i=YcY8q8+zi^DCaEneVTyrjVXstrH< zQEUGbx}{NGyHnX!tAWP*M#2jc^Y?2^m{Bp1PN0yO8X%$ut!W8o2l3;RYb>(G3}I^dAUp>^|DB7gKhQPFN(u>jTFL2o zU%%!@Z`X`GmpNIgDzj@V-X&cB`&fhD^5U0lLNMtW)=EX=qd#P~-y(!IElQtzg{Kpx zj&FStXBLmu4q<@SCIg_W8}9d|EZREB@qo!G&n(_dg%JVratE<`1DZ8W+s#ajlzsdC z{pgO3d_3D3eG~ICJh%D(R)*R5FayaJBHy4!jbwwP6i>^|-IhPlvS1tuo#xNc@k1?5 zh7%v`NFf$4G+j!qPc2wjzu3DPa0Uk44~LZ8Zt!8l@0I)97tT!y!0)|Z{I7QVZ>Av) z^?K-+;C^Ddy)H78u?;q1=Ve|HAO@>8t;|b$El+p|`1tjXI)vHU%~ zIK66ae_bD+CoK*=FD|i29;_k_O;e4zWc@@*RHcc)~g1WW|{s3heP&idM!fohuP~x4$HT8ZtI89 ztNel%+5|8gViIq_R*`nPm)uqGzMpUs(B+IOW|^&kT}p@KmPn&;?;NQYn`=eJN5yWe zY-DZvWxvco7C2`2$2Nm^O~iWbSBr_WIVj2m9E}u14i^sH zaUBJIZl12xNHi2E(ti3yNIXGv^Sw3;KPYoJS&>r3^SJot(!lD(~l z!Ehu=BuL{ytF>5ajnthekTONY7l{|9Ys7F4O;10{gYse{j1+`5pRReM!X>Qp_Ses+ zAP_dSI$NYW;}2IgPJ31T{rx`4LlaX}v6i&dcPjf7ImV1^$I#Jt1KpkfKSK zlRqq!E`0)7YoX&>dMa5x8+yz>KU;Dakbk@t=ZtxELTbZJrP`RJ%o51)e5Gr-d@A4r1Wexqu;gFk z5B2r6ee8z^tVdoamBP`mvSM0WTldY{VMbFty*Wt|gv^U1Apz7wJZHEH1qk1vSHR-c zKlpY`j9OgN=pJ*9$MYdVn~khy#(ev3h|Wr5{$K+U*_SS6qLHADy*bHI{e7{kn~3~= zb_RSqv(tc}(}RyxdVe=H@(A?DVxtSt&$z2iO^>{4 z<8J+2&LJ7^5jghxA^CR4VB#K~T+ei|Ah#?al-1Ud3~XGNt4=-Y58d7y&u!T%ECQpRTuY{UX860|s{WXj0fDRpe0qCT0o zna!+0tQ8jD)yUv^0=2n~uM#gtY<0<_21#BZfJo7nh4zEWDRrq?a+NCXq&zk2E6eC; z;=gdkNBEpb-1EM=0^!>5u9Qvt^|#|uM&}!8AEiUs*2hUA3JS+Rz(z-lc#JUk&G0mISd=C-OMI-1_2^&l4gnFZM z8&EmPpn7>VHa4#binw9Ua}BD&F$Rv$A84S`ro}lKz=E&P2UEmz{%vlZE}}O(G(oiM^T3 z+aQhSta05C)?8K+t?+EJBU=Ktto&d#w5_eJWEMLXayG1=)Be9ECOWG#L&|Md(qJ=r z{=@)bkSGNHw7&O;o=OFd3VnT;3N=A7-A_5jWACwI8d!9XJ;?ABN}Wr7=D%xWOr3Ha z>hD47Ta;ica*{KQV>GJxdXSfqv3?!|ymP+lCs94GH;nORMWiaMr7 zBON{k;a;)y)0Wd>8?O>8R|i;(m0E~X9!_$*pC6m^>V>W}6l$0>L|$qyWLa0@HC>}p z#(aOkIYIt@bj@PN)-g#C9#ZB6@Fh;#wvSm~Mc8$IEc}_-P=D-kec8L%7+S)I7Lfdw zl2ZKRJ(kp^AMI~2fb#~@^x>>HcP#6dqzrR`Lh<1c*-yN$5swFQ1t zj8%pskH=b6{dc-Qu0g`Zygg>^ikoS~NgqNU{me2uNaUr1(BzmuLd2DO5}hQ)$Fn|_ z`-n6@s58QI*H?Z0jzW=hkgC^iU_`-E=MXo6Dj_25#( z%L9%NlmQN|fLCk9aqnW@4e#moQFHs4fh!t@vz)OvwROjFEbaiOVPV$R%K>5@gef9u zBuQV|-r#UAUdW#PQ0Ujg9xVq)5#k2Ik1Exr#)QlO6bsy1)Tfim`|hY3tzSPWs1YmI zSqZTHqO)M~Qt@?ia}~xufb%eCXLXR=It{6`L!VHiXswzk7W77bg=@m$fcF@id?SLN zi34&-jABn3)!D1;_bJ$}hIJOdes6!@JNlgG1!!CT0>=q-4lTsT8{k(nxxA8}_J+i8 z4tEhX;Mwy!Lk%DIEb>p5ZDwUBL{Tb1hbWV=={4KC2Dsgqm))tL-NbvIuvrL$fvIn< zQDSj+9T4$!ZxcT*vJ&%j@;=0lz2rzk&yuWo_rr&GEY{=smidz?Bd6O6OV?I64z%GN zI`bpN!BXEV`0_R6A2E_cj~7PU(BuY?C0>>(aPSs2!axuVborUJ&8an>p*7O$jA1f8 zF%bqBEbjD)>^Qy85(uH1?OT)wUl@csW zWeDkra>6v*!w7qSAL8J+Kf+ciES$g%T1Bu9kGaNxvsB(BM0LLiPS)51i2TBS(%VZ% z=;-{1i;W7tVJ?@4(Tr~y@NhZ4N_U0!K)uIeZW(FSI{R5j3sd9PJqWo*w)x$0{<=Dn zX5EGdTqDGv*?K)^8v(!9%-zg@dU*uAaH-wi;_%hw~$DhZc(}5 zOn~^k96Ua~rr=7+3X22Jf7c%r1kazPXMi5Rv&uD)%I3q zd)#tqj>IbtSCec}k)0{`tR*)y_BRY=~=gz8ekgs2Fgx02YMIe*ll`*cRo_>1YR0jF~WAbpHf z6dA1_qH8-nY9{*>6!hcA;>{4{3L>3j^;4x%K{jV3+1AYC?cNc75S!C(WJ$+e+tAi` zTf@D{!Y_CC?{p>2%uH8TS52q`BZGeLa4id9w$LDEVieM4wBhBW6hUI7q)z@dNbfH$ z&X?~G#pUnO8LfFS;3}m7jpb~vj$dFi7oSVVXrz(MBKJ-TaOjj470e2b$rr%dg?aNL z8sO0>jxaU+TJmbVQ&U@kd$tmB|3?GWitd5alBKBmQ_YCyc7n zXd%xav+h#LO(@x+$z&joMyo9u>{t8o!jX^pF`DS?V>GQoHda zjAh1iRl(!9tXWOAaM82XCS)+Pw>LsJ!>I(s27y}v(gZ$_Lu?=0dm8U934$WIFiffU zlADqTi`kr=rAZ?o(2l}E>`H!s5F3UY zSOF_qhBFtbBP(9W?X%&~5&UQ;g}BYNpf-c}XLB?L13I4>;N>hQs47Wy^~iEw?;t7i z97tgz>s6UqScKWZ_J>m0qRIez+E1ClvF4{oh_*rZN963DN4uR-p&nw)N8P2bN8(Sf zwBl9mNGRp@?TY4mn6(%O06d>Xaxd~5uzp48m!X!{V6uogh?_oGk?JpQKFJx$TFHvR^%3J(0!f9 z48~-VS4S=jOHGZ9%?&Lrh0rfOoT(pLdSbQ@L6YYr}DTc#hiEa%COftATss^6qwB#LJ|N0$L@EXo4qS5QyEshAm0>g7p;#L-6S{`P%i@l0_LhvCr;sUTvcub z6#!xe&TzA#JVoET71Y3Bh6E*@Z7hvAgQW^BHM@i|`} zE(}pipJx1?dd6UV$oy@nAROd+4Y2v>6So60|A4Y$AYaXtd#Mrl1mYRYR@c{U@2`88 z6rS*crPTQQq_0n`O#VM^5LP}yIo+2>F6mK48?xWQ7m}& z_*(wn&JX3XA7j_{WXa3R+dY`~t(18<^TEZTwx-6>dGa-|DLFgl92Jr5--!R_GQ0xW zfFXbk``g<{*@ffm9I@MD3aoi2BMKCH3VU-hZpq_un!H^{H>ke(=mZ_o@_kbEfii4j zIJCOM-+aDDMwdVkY}P};Od4ok@s&=!H9xsVbc3HeSS%O#gcb!;+1J;{uBd!-vR|t_ zkkSFYSp7*%vEtgK=Qsq0&vGwv90}cro8xeR>G+3#sneOhHWVef${qV;sgB?EHQ47H z6h`2T0xt)g=Y^jF>baahLL3p?9LjtQ`-$tVl3m*9e64Lae~u-Ami&ht^L$!LW?68b zQEV=&%Hz>Qcinu}K;avOD+Jc4QEZlf))O(rYuUb;l1Er2AVkYl7pXPfIEMwDL@GSo zE0an1G-%(z`q$832nWaL<&B>oDO5BY}F0U9v)Dquv?e zA*APsx(rX$V!BT7i2KU#g-tHG@b<&@Y|Yn!EntRR=u>lhi&DYhUS6&TOdm<@dAQ#g z-2}nvo6k1LsUA$^Zylp*aQ+a_FO}Xo*Z{zN0(7sH!!KVbBEh|qv11}4ywu3_Vkhm* z9=_b@4hqn5@E>PY(9_dOCqbcSI#)eZ?zD$|L2+y6biUj^>B1~lgK8oS4h{42-j_#(<2X*5qd~b`LlF} zV>zvs^n@EDeNxz>Ffv~$;cUl*mTsiT3b#&7$ZaJl81tAac}hZEC}U$`tE#C+-nskV zF`CY+ic zgwuY}MN^`sRR1%9JRx{q!6!~)l3hrnd^?pjjs($lYN-vyEuxktL(G(>3zAjOd?5>^ zV0sGR1+C8tEr`-YcEE-HB`Xa#TQMQ zn_~aG`=5)HIyg-RiLIUQ*ouY68!r0*`$3PJi*mrRTqHJMa0nCpIG1ng9K)%T2(uI%!a!{We+p=boPj zQ0=X(A)3>jeQfFWa7O=?Cg|Ur`v+L|gd)&XRF|l>*!o1YPWW-%AY*H5bG&DWDxSv; z)QyM7jn0MuladvL&qVyM66!w>>v<=VqN=6)Rown^z(1kGap5i@IXXF5LEc>Y zTUcRZ>G%4ku+qcD=@(~&|9T+7lyFF3OWick*Ilgn8U6Dc4uA#hc${Mq+&N>-^8nzedP$1!3_Gs}(xw!A*1NdM}>$s%#!2p#^!q zg(4w(PGbfO=+;RHO2TY;V#UTPp?XmP2YNShj)3k^TKwze>0?90VVWRbvBbHXo0wn9 zcr+Q;ZQ)UGONC+0LvxWF2u>3eM}bcTw;p;f~Y~wUm?`3pW!U9x2_;tXwW^pD6fDfG+X^&aq*~Lk$nj3C=bb3=+v@C zul#!Wp|*tGI{N?=^F0*HQk|U03M1iq9$b;H+Sz%5OzD-hGwb!z%J49?#nEkuogvXh zZ!ZZxJOG`4&dvDm$@BYK?xzQ(79ryXrP1JrO#&tZB8(5u zN`q)opAOq>>=@zqU>4q6BxbBSib$0G77+LI$S7>8g9NdAa zBd>i||MyCGo>55r#(RiqTxPp2d%_%$my1nMSim|ky#-oF-)oU&n!l>i zWTM#k!eX%{#7B%Zxv9v=M7%@&Cgnjyz}`O4DDQV^Hqb9R;{CrO0UI$0G73K_#PO&^ zyk*_)hNmyv?dN6E9k9R1~}2ECjV;scY}((a(1}BPb*L24@D< z7bXnmt#^oSEKXS6IF|1vX^Ys-0Q*@V^=#2Jd!ucHUOcR>^fL-(6yN+-09N z14rSyA54T@?Z?nhUmU5!^R!LG(kiF@^#ahrp5=DG_IC$TXm24Ih5MQcrLnG21Q6rP z{cD%##6&ZoKS1!N&f>{^yFD;jJ~d7pfZMc|_vYHBr?-b!(vO$d*QX0jh={|2Wm!fd zxB4IUcPA`6ikc|gsOi1qELjg0LcXe(R!EDyHoEl2_;-8^1v@lBBRPvm<0o4rqYe@k z5<=(Df&wYzhiOE0{en7z0UnzaMzgo(^!5|b3UHeK&I56Rr_#WdTBHo>DGo`9t?BU`E z@zx;}SDf2=hb6Mu#heayARJOs;rY6n_q6m{1k+Jddmy~maI(W%ELG{kkrwKxwO zo`&Qp;Vo=!SWE(7YGkh3{PO_OY`2E>XT`jzbB$Vh*WjAMkaq`vXD0WK^4Ms0la2+4 zgwY3|p6PK07b@Q+Vc#OIvpou?&*Q`WC!rnoL_T1vx*CDLPq?xqF{mA zsk97a*)*r28|NuAY|H0L-ipKSr@iFxJlu>-bn%RO2ekQOkrqv`b4$YEXAH=e#P~jp_%^2tp-) zd@Z)FHVCfPu^01{OA@g}P1CdoF<*NY-PT5sktyxpgq+XzoR>*nK$0(gy*!UnDNANr z8#`uk+L^59dDt&Fyp6YAQNACg$Uf0gL?g=7!@GvAu{OE)_zA7#kJUR!I8-YO`@Mh9;I4jBC~YHw zJ|>_wmMuYr-WKpFCvI&+?x-(n*kpwLe9a#;k(sH-fYd1YdtSWPD%Kt*yP4uTu(m%C!^Ll(h%@ zPT#}Br8UPgW$IA|FuS|Dp33hyU0qjqE91q)f>|rblz)aW+~c~Jy{+~-Sz~8mGGEs- z)LeGQKOPQSTbaU-cD;Hxn$hRd+10sHQ6*4VW#$^?+*5#=4ep&yDA@^&w*958KdG;6 zX|>^8&l~vFk#|hm2z1hPWgIsL(_0ofC^iA0PJ-|}`0IPq;{$U8m(sj#D`!k_xL?a} z&RwV~>}*OJbn-WrEEpmD(MBt?__Fo$;v%2k@Bzy}EkIPYY~$S9lh0lBeR91g+UR`8 z@q^aENbt}kmVjo!y#eS|8}3%VeWC!gk*Ohdoki}ckg_vR6e9@ihag*{ap*Cn5%k2X zw&}6hG;yxh06;qsM$Xx7|V(^rAidoFfCT-G- ziWa7t%2kuyldCdbKJa0_Z0i(}{sXC}%pxjYjv24tQj3aWT^KE1huEnw_yygkN?vYH zmKJ6Iw16AL6I*;3xMUM!hKeeo@Jd1d8d zmCjY?GMUoMOrN)|XaQ+E;O}`xgmZ*{BnU*b%n0GFQ)`IQw0fI}@C4)|A5}yU8;mC^c!9HO@ww)a&_@uX)3lCv zWdnVz0$Cn~Jj3<`dSS5>*i`9Ne6F%*!`YUvfR?2Z#*sND={e?fj2jN*6YiT{6iVSgadSJMc@ZQ$iZjiE! zm2kPC^8|`4)FTbLHM-v9(RUs3g%Q{hUq6OBLjjcwEVd{1W8PDxSP-->G>I>Dh6)dz z4v#K&NRmRPilwgZ2L4yC;jGm1eyCL>>pk$iSop2>HF$gOVP{iT4AS#8h}*uId^lYM zX^a+fA_WGzw)qZ0dag-;Gt!DR)xY1cF*mr3#AGI&okhLu!Uym8YDUnO{Z<>!{w}BG zmgRWo;J_zTf;@yA7Mm!$&5yX8BWYwb+ecnua`L0zN+sh6S8R+VJ+>+%YJj9EEeXSK zQTryfD9^+v*qa#DB&-1~No|8ar+*H3iEOQl?NWD(o}IM@9JE6cIQt$c^#tM3fDJ9p0MS zUP|}*mPwnC9Q@LqtPmoQ)n&I@9gmsPkT;1*NC=NkHt^k7C&`P~{)`{Ehjqpg78kd< zx|*@M`!y*EhGnVTfN2ApCV+T5!FWNx-Ng(g&GZk!VOfi4c(&T|Q4ta}AV7-!P5>I+ z`C_)$gN`Z9yq1NP(PFY^F25*MOGxADLxkB<|kx)ALYfrASb8$%QYm@Hg zn!pXfj5XH!M@UlA!8_g%zlcO*ult32V@s>!p z{6%h|B{JBl9!v&VIl}HT{--sfxm#p8kd`>1{RmHy@2CSu!%b#J;D8Jg(K}2LZKhg+ zO357=COyskE4_{RyJUUPTaJU3qDR8bq@)Yu511MREOixUQUD*pe7r}is)})xjvn$2 ziSWK4Nz|XYC-@3X8>%df_~3Hq+eeU|+OyWD!w1+9fu&@|*U099fE-@3CTEm?t__Zr zbz&;?`f}tP4mCP7EY^wallf2+Pc&uzX6nVk0*}!r6n<{Hqce+)$DAjUo^}m1Z}NOu zX`uMb=>yY_fJ*-&s+d@M$pJ5&)}AUQ<>n_~c~lkpm_fzA%RZCoz@v4<+gLahvOAU_x*7i59Q?+5;{&l$j<5W{2` zc;?f*yIkYuz8Gq@cfGi9pl-p5y4bar&c`54de$1Yq>FQUhoyKJ<@5j$)|{{?Wv9aQ zy^ly*poT22#<2&&NS0P>96rO3sy8Au8}qW2`8ln0cM*I`6e>Pw2sn#QCZb2`YEg`| zm;u1&JKOCqEBunHq=ka8uRZ6mQtojDbkjMjV5`zl?LEaz5FdPbzZNIuQ)I;iCLY{V zEvhyO$bpMu5qpBRwiQ@5u43D*G;`x77nF^#X&>p5NKpx#xzx|O)ic0_5wfw(@m-w73+TZOKZd5z9?H&qq0uooObAhTb7Q-- zOJF$woqr#|8`%qghl~gR52LbM;Dxo9uAFusdaI#C5HHa)VU3=E|z1XmTyJmR99% z2y{G+3L9dwm=(m(84|Lfl8AKa{Dhw~LvTKAf9ihL+~!sa^9f&FPf1}W6y*1s|B(J= ze_Cp40-PrPnkI04Wnbe*hORCyRN($}!B54=n0N=wM5f`Xq+(j~80|QP5~I*fG@Fte zomMT~oXlFo?|Zb^q(LL-86SOImqmFUZE08MtRCDh>=v5UYF1q!!75zYfODKr5hgkF zD7?nUBS+<8`%6CPodl#^yYh+I!K}~sfT3jA$ys?)4R^b70OPeJJ$(P# zsfSNvP+_n`F2Xgy$%84Q=;bCI#4><8ZIe!h1RU^ z?y}tsE{5Yl>C6N^kEa~Lhjtnho3-Ads-KWBvaMY2CD#chGy>okPy#wC`mqYr;hYP5 z`KPBUC{^`iW770i)wPwi-jA*Ph@GXNRU zaCLo9xEYxJ5izI4l+fO5yzn${nx|}C{pTYoLIBXK`~1-8(1F0)VcONzWjvrjPNhnZ ze_p6kdCpO%FaVxC5XW${Qr`&~5HR9c6CdqoR7xyQ`A8JjMZA@+Gy@bhWNKvFBC>Xcl}rilEo zS?t5#y1#`(8=k~PryTgOrh&L8*u-qmTgfS~Ih~)$*3K*FZpL7!rX}WPaksUMn)Xn3uk7{B1>q|^#V1qcU92q7!N!1j{- zZ!xjCX>z`AlfQ`KNU8MN{g8Zj*)~)2Xaf~48<&lqEMQi<QL<1$!~*MLSLw*~7+UMS#r!!5UP~$k`XKOa zl#}5o{A__W&4RptQie%oK|v(d@2&%sBwAjCVkMN+wc#5-NLO40$kQ1al2k}QO22$O zq>JMRO5frUq(fohh{8x28>1h8o4JE6G&L}TEPTi}Wwm_5>2|vzP@XVILQuBl-#b#0 zIYNHLWC{sEoO;^LI5El!Ksjnih~J}O1C`6~bJP6nj>{$GhUCr46$0nUT&QZHpvCY zsyJAT$T9}9m3?pTjkoXS)LiyV&4>u(L!68-24?Zt1!39g3_d3P4LtuMk$S@(81Sd3ocg|=)=LI|!wmU86YJVD?f`P%}$iJ1) zKwC1vvBVeoBfU-|dQf|bYKHf${4$gZL+aJ(7z*OoBsQdO=aHcyST4I~{nI!{kCy&0 zVyEMII*TOc4{)+ZD6gA(rI7QEp(RPNr*Cb|z_5@=>F|#^;7XVKKJoqxh>U=+SVT`R)`STy?Br_(tomWCMGN zVFpS91TuUd?IEvvWT4m+ETDhslyDADYrzr=Ck7EJ9)A2rrGM)N0S2@g`=*-<)NXWG z=ToS~;pOF(T?`76Wcl4SVJk(|CNA#mn=Sg1OvuLyxx)&wRz3CjENQ@89&y#zE z;Jy^O!Rw{OTYNt*jOLg3${odz64@2e!QMO zTEr#Ma8p3If`sE_H~gbUd5Z-6x5h|FX-ImLEe}&#T6%+W)#p`3464cgLP9<8qvx)n zp<#vg;YxB8VrW;_(;U34X?JLsGQ_P^9j0gZT zdMLFmZ)g&EWtd(6X@$&$6Lp9j=dHy*7fOE=6<#JcXr`rn$D$+0!6WZhwlKf@DXOLDIzW|UWqfw z7%is(4aMovkgy5Z;V<>(@8Y2FDwH=vdT{@GLyg&W6nT6wR$NN?r(Ds4|zhvQf1 zAy+m`lql|WRfV7F+6pt&QF6U@u&8Js1CNa}GC z=}KqSdt%ArNYolVB*_-;`mjY8`MCg?1O)|!u{AgR`m%B1G?-5<=4i3vz5QT3ad~m1 z2Weq~0f->RE1`VSmHeCmT-m>6!TbG37#nGF;3fnfQ;A;FyYDuDlM4;_R>33%MDICb z=@)f?(#g5QWnyANlH~lzVK&|L=>po?Gcfdr@w}|F@L6(xavcpVt%YUri3{DEwpvx^ z*wD#!ZqIt%&{%x`NZ!lL2?okg(<8N{H){U zT%m~P-+#`qRM!rPiT93(j*JYIM5U{+1~|K#e)j!M0)4hr*)}wuvl9MCHFYM1(#`#B ziNIDO00a~RiEIjlF1pp>Bi-zssE?SnzJ@ja zmfQ#k$Mggv$Z$g9BGp9#s*|RF7ORKVP({%RAuAn_Q4I(HGHmvKx2vl^>$P)S9*V^u z17e_Yzj#7v0@iPA?d_t|wGF(#TrggDX!-Hr_QjLxv}#{u|MUQ<>=AUJCFzqo1{zcG zwZQh9KT#^G6uyS4D70k~&i`24I%@^~AyvB?%)BhhZ=RbmQhAN@U-y9NVO5UUOPBHehb=j58>6r|Jwv0Y9G$;A-+V;SR|VK(_m7JI z20Sv7G~gTsAQC+MFaLF6e_l8O5oorE(SV`MKgK*{B_H&^O%#Wl#dytW^(gn4WUIR5;B z^z;kTsbdum+pn3TczB<_fCC?cSDRBnR|bz%M$e_0P?sYJUOrk_^e+6NJO0NZ`?BA(JCa$rlePOt$&>74=SmS5r%iV6fcfPvInQDltMTDsdT`-2)Q;Dcg(_ z@e(eyZ(nDqDB>dHAiq+{P>UI}R!09Cr~QM{{lBk@GC;2v?de1w<@Wy>`u{ZcR1BYxf0WCe(TDp&ftdX} zjxUOTd-b1Z02B%$J&h-C%tTeReANV_oh=e-JVT@)i@E@qJFEf;j)Z}^UCganOEbVEIUXQ`?wDM$%V!tpok= zKRyM~UdQGnQ2~N*fJwoJ>+Rb3_kmCS*aAOLK7aH7{Eqx}Q%3z!{Eaf8@T~&eH#2?$;UlgZj^g@6lQO zeYrspxo?>k*@1jj)`2bGq7*n%b*U`ol}+$57o zXcI^$cTi5ENu=7OQfH15I^;^xzJs z-j%t%69ZoV#8mSC2d{aM0sc!qK%2F2Cs%-aDamkW^OoSF4L30(8(UqeNowv8_QKq} zxJn+_NOxB^2Rr9&_2>7%Vab6S@P4+71d_RR&CH0)`~!4&aqQoz02syoRM7$uNOlqX z%NR^#WYz^VYS>?v0O@mpt?E?i(b~ZRzsmuM$kW8ccvYn$H80=~Y?4u?o;cs)zTlP% zUZxYlS2qUo@#Du<))svHV>3M2w702^UX*3@wbj)Yt6Hr>A=~%&uI+>$KR&Fsbud)e ztY7|W>u{`P^%oWfynPg1-~0q91|ZIgjg9@Yv9ZQ_rn@sc(7zfG$Q!QTcGsc-ujlcJX<}4duE5x zC*Jf8zB^i=ifepc`ncgUCl3GkmB%wYU57g7JdL2IPa%&ti9>~1OhyJ{sl!6UKT16G zuTu1Pcmqj|^ocCy@$c|{#zlrJv&O4OD*{gX{O6wJN^0+Mf$Swv35u?Pv1N5lc{hxQ zK=y?_`Fk>&$N5>d72$X|o`t#_^R0uJr1<#gXxH0Q2GV|YW^0({lCYs|+66`?CLsS& ztth^z$<4##c~C8#j~B?J3Xeb=Zq+J3Eh_P}3J8mdF#!0As18AWdVt|?ZM=E6`%)bg zRPnjR=)kcvATv`1;73l+3ul@&Fvvw9H|gzcZJ(cAgv5Rpjk zPuD0+6)C?w4p{SFIFDH2<6AK>*g(F+|B2*)qIGp$NM|rvUw?~~ z)eXSE?U*5OZr8i<$q6Cl^puo$tDXU48rkU9J1fG}P6&08kwx@A9jumL0Vjxw!Z-x! zPrCip2!L@4I2MCm1iluo*>tBaLofin*SXz%0L12cUxDt(!0zyYIm%H_cQp z{$O%BiZD`lD@Y%tJs!9XtCN^beIJwK;;3vpLi_1jH7oTt;=6R*7jh&-Bssg3NOw!M zfZ_-z_~3HqE#BUrhy(5nPkQ`pm+psVRlv@Bs+L7OBq<;Ve^cpr7j83GIX_5s3 zLqlB+2C1`z-(kdvMw#F|eT%V;Qb~*OMvB(;r4{5BG!C) z%e(MWQrC@zgVlyPVMN}-0{^69UVqr5L+?nD@U>Ys7yRbvBl}Tv#At;?LK$IjZEY<- zKAQLGaB8F8-E=k^621iQ%kgpmJ!YPDxVuBA1l=yiV1tnJqkZd6hM=Bm+5MGog{TTK zsrJ;(AV3{)#~XC6IZj!D{kh*bAGD_H~u-KHHA)J)R}LW#pRs6lROeh0XqHZfg3_^ zhu00!5C(qv)zH&PFfk091yQ_$`Yo{lmH_`sy-f#DX6zH+mq5z0cKIeYvt3#P(a(`b zhf9s*;KhoHNpafCjXG0nKOGJvlh~CzBiV)CUin~6FsDi;pHCyK&^~V;Krs6IO6?3s zfI#`Kt_~zgO)^87&y?OlFkIvVHxL{&Sm$?9b$&!K#W7OCRXA``{;gu&#jT>QbigPu zVxy<|gN68u4U}8Ue*uqNG~iSWp|NwK<$Q3I?V_Ya5(xNC5I?>O+wF%w z%}HSm4Oq-(ViGLL>(X*cSNfk|kTO$q!|Li1wwsg}p{nF7_>dZUMvL@_A5knTj^!qT zl;LZ?0M*w=yFwC(dcX0R#g{d+nmzhb60(|KH;tgv2%o9)woYqtI@#OUM91g60Ku5Q z>(~uxWK2w>)63cg23?kNZAFC*SeYwr65IZ4+esR{9wHi=`)TA$4s|9{6x-2}MRW5( z?V06R(|6Z9o3BQLI=4qh3-hh){&24IfPOoF1UN9*Z_XA-BnVWjFAYuQ=ylvT6Py$l zyObJ_0Qni-`=1C&sBd;sKaD}KDCBaVm{=96d*^Tw>&riLAmQ7RFmP}$%rDk^&@PbA zPue(D%g>{d8<5@qhqY?&t^s=0y=U1YE$JFK4VzzU!?nig=a;F>Oc^Vl?FeRf|G@Ic)b_bjIV1^JQ~SEfViEe4S$(@O*@TTfK`9Nf6M42WbAlu<}NR(gp4PCbXi}=pismm<(4f`+LDi-Apib557F~^7Fa0nBO zvM$e`^Di56cok#KU*ZxHTJ0tHT$Qah=C4DErq~m9sw>+?u{isOm6q+5ZD4&Rd($!} zcTV*Lwxr^|omWuEo2-wob%<0*kcv00Uhd_k#a^eS`EmS$Kk#H>RP?MQ9>m2xKI8Se z%=KB_RaJd_i)JG`C_E%!Z@<<}B>%ED*g!n%X4Olm=vkuDY9;07_Q>mx{Lxk}pLv?9 zSo10%kSHn>BKR$QlV(b4SSXyF+3p2I$W&5>?ubeVk3ew<0Uco7zf^8Rz20Zt`hXNd z1?Y50-A4E9lPMRmzjy3FG=TUQ5V8$!K?{)(+{Eov$@u!M-NEU&2-> zU%o(z$MKN;^3_#<%_(=BuYpRwARtuIbVoX3jm7mcP%I`_Z$KRU2XQFPL{H{+{9hx9 zBydpBks!?TtFeyxYN|&DU(NLw9<9*F%_sx%0G^qV&f8KK&2h0`dxX7#yT-$bp?k&r zEB$qMhcf6w%k@FE{q&6QS*0;~ud)keHT!zDd7avSb$9P~+`JF6jC4<8b9As#uX-NI z&W=^c^D$JBxb7$d)^r3UkWOz=xE~!*5E_$qi))@rRTmlv0fY!aeXbjjx50s>N?;~U zEz78Ho~Aogy06U(=EOe(l_-(y_)9k|xVKz*qm&NUH_q+|dTdr!56++|zdI`XeHNWz2_9=?{DTdtWcJEI5(FS zK$Ax74qX2*PSjr^oqFB&!gHr-Ib^uPQ^>&;$n~OH1p?}*te8t3<#%B^26unrsY;{; zz1I2?-t^p^4P=0)#mARA7F_04K2GmwBn*`Dl(r@djTT<*AnrN4`9pSld_@0QiNoGxX-x4#^rry}8dGU9G@oE(c=qcfrTE?#cecjMRY& zy(KvL&+I7(5hJ}H?yIu4^%lltGqHF_Re-INA7M=7_?!C_&8cE_yO{PqpI**&<~rR% zOJm37bIU?OSgZMLX(=c!sz^Q~zo`BX{gs@R+C}GPLVfuPRM*}q%y(LXuKIV7@$4S= zdl;$>0i9Wz#}vo20BJVMBIldn{%|RO&<7Z&fWyxZr!naiD*|cJZX7M#TkhG<^iwD` z@RLcg+mk(c9D}tMOa8?9{i1Lc#~CI8VXiKrp`;Ht2h_C(x2qi-vj$*9=q=kX%XcT_ z7wCmcy~ESWl3%sXCa5YBQMz|Gi^`;At2#|-RWQ~Zhvvg}y)=F23Jf4MwVrzJGwd+< z5DCr@}8+@QEJDTUp^V zzklD|?+^zZqht%Y6BNxUq$5yKxkN6y!x{g6=;GE$0}=+q!4tJs5QIB#cL$andt?uV5@3lYQCRop^y(r!U2HXn!K*) z*!c$p@~KVL;_sawJz9y;DCgJRyTYr~I9w7>IDe?1q@3rgf;zdgqV1|1lT?^yi;4z| zl@JrDVYzwe8bplnq+ZY30n)pX4ix%nXKO)Pf1I4bwK#Hgz$EJ<8`1=%BR*YzO$B9AAqcIvDZRGZxT)FjBs2KO^RqSi8Pr_Q$JdY;*Eeu0)WZee6Fl%R z(Kl%y3BEqJ8<7~0q;IPR9~+sevo{p6GbXkMU&Ua2(C#J{!`NxBxd|k4dTvCJQm_47 zSd2`LZ^Owxo7o3ovo>cz04vRGJr7l zl)Y2vKzCk%|P!y5>#`fAH9)%qaWwe8N2ug!#d#SZL2_$%8v zONWqJ^3mz;CH+H){RIUtw`Wz5A}9%Jf~RG!k14@{;_#kdl0L|p8UyT;z6yeo;Lq~} z0>N#CDj>mLMT;s<=Gx=210k1a9a;Pks9kO6U9OA*TG&N(<9m5wezn&WAp|KX8&FiF z{JgrABfdf@;v4CF{KA90mcC16Rk1rDBXq7%LOziz?W*@#kL)dG51OLF=hvKS<)ji&g zyzo4kbq|h=GacX0ktb!TRFC`!758csUAl2UNdQL#96!ZRPgd}$0#=~T^UrT0Hwnj4jO(M&W`@QFr; zlIws#5#Z?+$*th8(ZxdfDFQkTD6R=rw+eaXR$ScAy1&jv@@8UU#wp*Fj$oabyg*Zf z7)Rfq%HIcG0#FSMA1TWUq2<`J7-moeICD~>E3S06vvH{1qqdiv3c@Y zsDuMxc!-sW7j#$rUmP5oO%Mi5y~J4;UY1ExGqR;(y5Y+avJ>}Uo-{T@1W4eSAub=2 zPdwgnViY&|-sM}%d~GhxGlL0br~kpQv4X-FT;@7;GdMi_37d(U8b{oa(WL1kQi*R{ zeVwuHMAWCJ2d+CowoJK}Rt7yzD^7b+-U?6!mmt8%hp&d{3k8Mk75-HFVMEr(IaSos zn8!l|&rLWvLx=A2>m-t=l`;|Q?mQw|4$J&_ZtJoY@$WAIyy{Z;i`Wm6g)AaD(?CK7 ze~@}v{}Z{K=BAkXP6seutEGu#46@1GEluHhx6xHERSqiFP{Q=BT2Ol>lOgAQQt-&8PHiHdy z#ojI=g{96tYvw8-9JeQB z7N}nU{;W#hh3ep1Jr0n& zA|1W55_V(g@Kdz~g0p}$^Tl~p5?o)@SAOIEfhQ7E{-C}vT^_LDX>?g95md`gS>!Sx z@qzIL4LE)O@{rFrV*gKyG6Efldvklb!cwDSBm=3x&$oO79h|eNakN0v3Wop<+M|eT zj|YRSKq9$awC@7d;*#P#Hs^ys!#tOQgEZ=I12_nz&?aO~J7D|7j6CG|a2O8Ne2Y~& zO3`j9;Ydn~Y08Trx640&6M7T$;Zua^rzDjEpb_(z!6V-`AtfZmf6sDAhLCSyonc^13l)I{myAY`^p zD%41<6QQHa^ z@dS&5C8ed~jO8OC)Q@0-JH@$Q349T6uh~fk5(ziV@7jpk^J-=~ZHDe9#uof$dY$>C z{fg;*81(wKClXUmEKHW`w76eF*k%sl!=>bD_oe%Kg~{+4UX=GD0kYm6j>M8C zjNyuz1D^Ull|?1YYld%o13*Cu{1w>pJ;2K&wc*5JY(rITNv316-WMuT`B7)Ctt;Av z5sRM$)+UttQYC+m+v;v-v??~Wh8!4BDK(oPNSk7^G$Y$D*k8FnQRN`wX3mGpTfC!;lb`)f*(I5A9=y?^aKcx4 zaxjw{u^FpB2IF-3#N$dNj;%mJ4uaPqYHo*u1FKYyksPZ=6h#VT9OJeF8OL^t7JvLJ z03agBt4>9W0e2`7e&9V5qoPg*JgNj%oPmrC9Zgwb4RD<5-z16-D>RBP9(M%Gn-zZ9xEm%Qd`rg+kKoO0xd^>$kl!cLt4Cn(sg^~8HY1Q0(R_y}QeKm_u zQWa97vZ^%K-ioXua|3pIHlq|F2ckat_0>S)NWIM$3~RY9mRv%FxrGrF>hqr%hqggz zOVAKXhDOvdoUa+rolhx8H1rS5Y;-TiUtRUh(AR%8^6ttzK>y3EW-40v?;tzvMf~C< zaK$|35plp~i6W%XHOk-wlH2IFmbT-6vSg6 zkutiQI2UJCl_b4^3}rqCGOs6&Z>8P(_S_m%=s5hrQ2;Un;oqt22}vVcyrpYuZVKEH z_^=oiw4`wU_LgsQ${!6zx^!Ne%MT2lCFn#d){!o>5YmDMhlgOp+M9Z~Fx)~04;PPm z_;0{91yBG!dgtfsCe(A4@o^(!9LQZXWcD5sMKoHSo@FQ96kd-4a_7X}?XSBN;9KVa zYSM=rE_&RYu>ZylrfpV4jjW@8Yx#0^(0hViRiZg!6^z}1KZ~J6D)6K2)Iz#C|FR}z z$fMXxnSTG`90I=hDWDR@iBm2HxRZ44s+27?Iki%ebAjar<@5i6SR!8Zl3lJlxX&2s;b4_N}8J^FFL=lyuXzUTOwhm&8dU`T=xZ zUPzlttoWMac)dzzu}Y1^s~u9lG=mRex8kGj8lpgV$9B;(bK z;Qx4=b+o6?y~RPBtknM=jqJ-xuY8o?Z4XqrV)kdjzq;!d;c2@a7~qQ-h{%vb$N(jy znmb!5G+_dH8OJ~xJ65Oo9fU?7m4S2<;Iw(3S5sGJthycXW`BTTPz0kJ$P81#2duPS6;B78f+}xjNqV$R!iX# z!_yEVLc}C{D8KccfvNWa!8frC(8{Q-Ly+XZ0t+7pbRYp%l60&qCF~~`Di(2&O0HG4 zUIbPgO31{tC~vXsM9pBaJb6mL9NuCsJm*rejki#z0A-mtjO|X{J>9MT_WOBsZe(Jj z?_7E52Wfry>|AeopJocVM9A*`L}gPxfYe(ZBaMuT3;B%rDFc{(Me7?*WX=x06w#B!M{&$ZK*o-LEl2Oj^&mLXHDw-Uvnz!3b?DvZE&hBjrOYPf;7Q zk^yzsv{@c-2PbiBnE`oZSv1v+-<5P+wXwwY8$dH z=AG`{#qmFThA8mzGm=G)j~uZ)A*xYzd31l`?jk;3Ygxd*{bOW-4-F(*NUI{7T3g?U zhQ|B+LCKu?0P_?c!qcXIxFAJPki&_WVPv_+axmS>`8wXQnRKiHX{Qi|`lt*w2vxU>W~1-Aof+9X+U z<(S398SUfwnizTsF|oVrMuHjZSz>8GZl7)0Adog|CKvd}Q{$HpsTUWiK`-PT$mufB zQB#3$zV#*6$C@!`JTRbAQx};kthfS!i*sX*S_`1ms_DN!iJe-Je%+tX;LHw9NObqb z@e$0{PYiJR7eaWV{%Y9OM4yk z`@k#>N>P0QykOhSe8-ZM?0gS2SZSYS$;Z3&RQ23TVK-++oz!z#p^(!v+5TUaep z%-{Loc%X^|x&E^`qyk42YhkIEQrd%m_%rHHcaeIBhTx$asCtGh{&fleZ+P&*dedsu zk&%)D@VHee97zrRkKo^T#lP7jKEzqT_GD(|k4CvMAzA0f`UTUOgS{zw=FvKOmdXm|+HLd?^F(t0gT)~v@-6*z z;CZl@PUXbk75OiS4fzc=fVz|C8Uq^EoT?$mf*Pt(<^0!WAA^Ai2+(>bHZ-^Z zbSquiy$sKt^MNugJRFSWrPTp8D6xNNpqLOcvOr}mJu4dnbGyH?QSyX(7Gdb1#7MuC z^ngD>HrWy$Etc4Fz>Byu>}x*(D`N1&2ZetD!EdOEc6JPw!|vDi_tjH%ZE5}tgDp}( zRL#j_2iyuwbK=3ABH<0qU?Ei9x zq8X2`=M1F-BG~hQcdWT+(OkYL_3h59=)(iNJiM z`Gf#_OL>~0PS@Vx912{R9kc^AHfw-Nx;NT7$;{OI$x;TC=c0Stu~VYK0Jy%Q9jCne z@7Ks+2Dc9at0;rKzV>pOoTMD$7Plq=UMk00$t9k*RP}pg6)88gCkl z0lVGiP|ExKYd=SV$auD;Vh`Wjsfw+Rg4!>w;*Qn^&hIjLP+@iyp zEIZxY`B>BZ4;O_d{GkfF$-j5$KX{jtFJLBQsMgqGI>RuQEyrEgeYG?&-&A}($HDR3 zBdr+`*a_PHGtD81)!f3woI16$vpW@G;d&og`>}x zXiGRcJW{{A&#!?;X!T&-#ZA)C(CBEjmLNbySDp{79Uhhj^613G#Qbc&oT6c1#7_! zJ4rED)XnO%#m~CY1vU#!gnEWzhsha)RA6+%P$?B&E}y;l!_L?^wmGOFo=RS;jifYK zE)$inq`8f7JL1yK_JreaGa}pCZx6ckCinN3-+Tgl2q$3c|HYS{%I`OvcENoJ_MoX! zGIzTl$3_R2Q)kw-PNhU;CnGG}J*@SBKyc^hrmL8*TVqleqBe{=-TKTK;Zlp)pep=h zy>iQZ5h9FP3htd7{Jx775?ayTrEHPrn@VB18nkMg*f_FflQpri)wDMg;nu zHD;t|^WonGngNCP@SKw zGU;Py2j?!L>Th z&&zt56a3jQ<|2!NE1cRV*GtcGhV0=~PTgMs&4t-iSBD@y_utE@ z21Tpm(l)Qm3%r1gOqo`rvZ<*l14D)LQj-G?nuC31I#Ll14$jTY+(mb{5n!6{g98T# zBkWQa5OH`&wV+fhL}LHD83Xz7!RdztuM54sy^CYG{g&+ZueIT5G;g=s>+w^rF2|ZmN+bd{t9j9sY8b7ZE}5wn2B9h84qpaW;y* z`E&c(u(`A z(P_%wg`^@Gg7@)R!}G;UtqS9Cu373a1{`9cQs`o90A2Sq9}OLy$M#Z?J>v1!{K2t= zENp`EU*qr3VC0MEbEJ^G=6~j1-z#onp^4b6H46(kj>wqK2(Pu$9>|N+e_FfTiGT&3YWm>dE?%rR8+kwYMp@fvQFp$hVR+@bcqvJFKX|ztgnND%I8g1Qg5_ z`DA^gh{&hj-`8_?v@H;_9SW*fng{ii6A=_M4So1zlNg)VS$Ahop>->lNbdO(i|E~4 zi+i#+*!`(e?7MeL+at-)tq)n`fWZTOXV160sziU^iNWH<#IX)tF1Kx>8)`=zp~|3t z-&Vgbi%`~!(RTUP!`Y_49HH!?){8%Cx2t0VVi_1nmWG$h*=iJhj+TDx$?5651aQ#@ zgWtK~^FEX|CVbEWgN7Vp1xj3K6C!MMg{agAqo<_oiv|-7Lyue(FX441q%4mFf;Fo& zt1F>Gy?bZWMN>kPxK`;tliGL#Ov2<+KqV5XXh_jT3w?QH4Qr0CySfzwtnHbS1;k_# zx5!o#ot?MbGH4$igTIu{%CP=#hkWqA$TwH^4h&(ABg;OC)E`~?X(;%1wxs~8Y_C;Pbg^LSV9T_sp4{k~ z*%1rL<1V-b6a2J5rSqa~;}fzxKOGU30Ii`x)N9ZjpFfk>zh<;ZHV{8wg=MKt8zmZ& z3yR!miRI;%V!Zd*Mhl@qNX+=Wkqd9z1bRiC=?|vHGGSU{>%0fqiutVY)~JZx^sx$h z-z6t%s<$+4Aq04KQ%OO|4JvoRh83$f1eT=7;wgy+A`S{-<;%_$%`SiqXJjDGN!W5* zOBe<4ZE2H{|4%b|AyKGe^-*HjQy}9J-TFfj)h^!N7ty)2>ig`_7!3yo_PReHFyVYV zjX?2yt;wv(!`3K1O(Q$_ZmC+YW|2{JJf=rdu|X-ZN@GUanaGc(6TsMR5NU)I?dC5V z+-~Y8OB5t(i3du88G9epYm1|@z0%9*gJ!g=^8&Nqq$2;G?)fT65RtJRw^owz)5>&J z)f1J9%vN%hm@vDJg+KoSrmEOCpkkDJ0mIzTL-TEV!x&+g_SwllAbq=1TX%NQQ1Oxz z&GPeoDJKIC31sWwuB-kRcBZGsnCN+1uPQe&_oN7(w zZ5y{|S*|xoq-U)&r2}zMz$v|0E!$YAU(E)?)O2s83Oz)*>;p}E_TRB0{szRFpw%JS z@r(uPQ(LC#KjIC*)GUbgd$v~pcC!m6x`mbIb4 zirYNkI?_wAwMNjeo$ygYguST9$l5h3Dym^4TSH&m)d2@(Jkr*bdPh;AbiQYrsvf-L zE2$)%R+F>e*8G~sVGe`a@C~(c2}arXPFGQj0dK~Yq3^8j^M>eLnvEVzWn`@F9VDH3 zfq>B(GU`KM;8Xd{@kig5XD)Z;^_Nvj{fG;6np7S;{Fg6YRLb$_vrovv&a-Y{t0j62 zK7>Ihl^o2@jf^>L)mM_^wXu`){X29TDc;px_Pa1nFO9OKDwxhyo zVtABot?TKn>Eml_VSonpnQBDvvfCdQ6x(msd9jmDL`Ht4HCMr%%25tcN7EHhvf@2M zJQL8CFRgg;#hHjy&NX^l>(~kVR=fo!)RFs68#@+9#)lq`+cFF-OMMV;5mSp#mH4?Y zncH>^i#a>q|9;nM{4lWz4oe)7f)p!AC_0*(G!D~zSqVtnm?+Og>6c7>Uh;fdGIXV3 zXEz*5lA0wxpDbwAtwPTzQ!ZJC!r5xHKlbV?wQz$Y&E{}={!yRs)4_b>ah+4_}myp_fi$jhr=Lt2Z0#Eur4xZw0}%U^y))z`Bt zww?C^B}4TlL}y(rZW$a8p-}lsQ@5yRTw0uiUYHo9LpN75(^p^gWqHN4mD%_HY`MJG zMOPR7sBb6y{hq3F-6yIv0+Lt?ulJXcSh-GL+@Hm5nkccEfO(`V>W%&CfEPt*6F4Beczj#j~@KL(ep zZV;8_+Mlhi?H?&R_}<}UGx-WkfE6Zv(~bHZCiOVJaC zhlGyqUtGSR>A2>n``#SUNLIJ~cC7g4`bmif$4sFTm;^D>NTAUtWb2jUh7h7FA%cGlkFtoXaOm8F*V%cJ7Lns~7xZ|1eOew8{1DQ&vPZ%pm(~|tH zZ4>2%eg;AY9?*_pBCv!0?%NpOW0OjY$aI2I8n%WCC65ncxtVGdDKQo9Ifs_r(Gp}= z55iegzgOGqDVL-d}0CG*>F>ffrUxV6z+*A3t47d+-*1aM?J9 zCD0?l<7w+SRYQ%gsl0P{x@6*q-%5pagTElI?OZg(ZLqq$>a}8gpCSs9>lmrm(*p$|Xvw~!CTlwuHe=oOiK{>1W?<3L{+wuk zwH>nA8dYcZCNi(XjVl;Vg-FowO@tz;PLXH+>7n^T z+(6@8QyIUMpKp_xw6yTE{GnUaiy-@`L(bG>|M#TwU9uBASULfL6Yi29t9|w26QVHD($b=)9(arpTx6eno%_6rr3vd=jr4iz zz7z3m;a7kp6V$sKZzRw<*>?2z7iQe0a68>aD0m+1?^`UDE7q13RH2iTk=-0D)kx&A zR;u&~{B9<8&_Lm5+disZsnDYo3&fGw zW|$Gw1c_USg@w5m10V5dP+*09cA0=B=+(-tHb`jlF<`}Ogv9x-t1!WY=q;FSe^jqL z@|EP>^Aq^?{d2MZNn54Xj^J}%Ue66-J-_u(MSURO=*TF1b z3B-pRt3o9u?YjvS*RI+=G4ACDdI{!9j(e03H3b9|L9GC8RUYMu>kziE^!o2Oxj)oi9u!7lg4Hmby-x{48dbpGtNCSg_q zJRw7i{JJ#YusDDYD!HSsRHNdQCHt^kB3tBnV(39RidRKI3mzp35cjH?l^T@M;D4Ai zr`^2SrTV)A^BxI)Q(M*kgpCEgM`nm{8&$x<#ZMss`TFP87!+N~Fa5j5Wh$|ukk9PB zy|YfHww(~|SuwMe^4oz=gRIjd)A4HzL0@0QB+G=};is=+LP^Byn>>v*%0_?A^rW7z z;*0-b#?{{|dZ{Tag3@5`x7k1y7xg)kr8FryOZlJeXe<=y-l4fN{GDgb9F>!DuHfQ_ zCGgiqN#=9)N0e6l|M+_6_`aUz|GTlBhK+3}jnmjR8k>!6qp{tjv28VW8aKAtnD=S> z>Gi$vd)$A%dG_q?*`1wv&TA&fB%mU&h~%P|)1|-dMr(u=1ZQ-iMzc%Y;B=G929jy? z`4B+d1@#*k?B%b@rbtmYV%qO!`#$lD6IjiQ>%wV;_LJuuOqG-*7+GjS;`G*vZV|kD z^Y-uI{`?-9ksX5%zpuAxYDli z#-JK1sHx-%CDCH?F5jAu$jdDiMH1fh7{(F3q8G=xV6*xc$U}~OxoCr%Rsq}%zq=;; zEeJWv*5;1w0$w~BN$Si~S=HniV5`cr!eE)wSxQUBGLWFvZj)taQ8z<;%i!TvxUe^A z7u2e`gml0BCR;&)Z?KaeRQ<7$+}VXk@bkwT_-GW9Ap@KfmD@K-2;$#KGO5J`B(THA zX(LDUENGmKVZx+)!PWK!k_h+m`u(VjLJ!TrWpY2TJ}S`3>;G*PS(a~c7_xa}m|u)^ z%(4~WM%K56ZkU?5>w}2-@bWS(ccKt+O$1s%&|YlFCK!cc3Xb zi{EDI(_4zLc=42^1S(65_DMCPRB85T&oOIG#+0NHr)PJ|>-=q@NVspF&|CJbC6|>S zjn+i?xID*oC3%_uoZr@!P>Reg-7g<#u09)w^QIfWJ{Sb}g7cl>il0=j;#-=*SvLxd=^1p(CjlC$q#ZU|8pOrsSh~xAhH^`<%}%w! z)_^O1T6O9QsRmT=glN$(EaH5Y4noKHVMAl;5T(7Ss>&Zkl*LHQ!ddfCqhm^t{~S8P zDD-ShEYzA&ZGK)YMr`_$9L zESz^mPoiMZUwVAdhRv1o{?T51{uN7jtN7^SGR)Qt0>gX#D>L%vIe7o+>wMgTTGbjx z;W<^+h_M8t$InGVk|ySFlU~G@X%~l4$9(w5>6JqW?RfOv`Egtb z$X2#wZ~BbIHQ1miqTA^!B<39ewc>OzQ$Wb|mHoF(F^sZ5*K~=rmt9a`TJ~~3UM0)+ zjvA{IK9bAx1)IkV`QFyGw(}!Uw@reOb+wHsYS9@_rCbKyvA!zpA{)wkGQ|1_rsg~@ z{dFhAADnGw+P=(ew3KBF*aHj_-_>mfessDgO9-@cx`jPj$ca;vXB8%QXJU)1#UrZ} z4BiA+CM0=#7Ag7X4EM{oFpN^9RO$|x;;%do8#3y>Ei&AFQCFu^#m-|E21tK4E;*?j z1vV*ZX^}58clk4T6a?W=(=9fA!76mkd)*`Q3zM!cyn^oBODwNfeh70f_E4pix%d~G zLG^62g-B*ya;Y1*s2;OA@+m^x%JcJH$?gL~t54yNmeQ5&7p$RVKZ=d?b4H&eBz#~q z+#doBVkwkq5TEi2$a@8YNJc^+au!WFjn{qQ5F(xgIRvB1geYUIcd)*cE+*T*Y}DJo zgSfQqAg{+53@uXlJE?^Q@cpt@xZv@=uaVNMAhsfUET}!Vx>z$*zQ6yyT0qG9+HQwR z<7pD0j-> z_K#ZRHxB98n@XoBxK;Olj|R@u&n+Nv-nOS_Y;5>oe-c9@Xf&&Z1lUvZ zfZ9a)P{9Lgl!X&XRKs%vPqWNF(n1%8KKjeUz()iG3p*{AhP!@eW)A4t8jEzic4U1^ zRT;C7E{YMoC!F_t6Zm`E zguizL6i`xB)%jx!}`nD%N$?rTWcs{9YZHr{QXN-Zaf~Y&ubvwTQE$P!8Mc4^$qxz5H`tMP zNf+2%YWxH<1QSNpFt;NvvHPkql|T2yl8^EGbD+iOgdn{{5<(b+ zx)y0b^C!aI^ctxPFYh@HKsZ&BlE8)2)=T}fmP4?So-G&^6y|4G_f!S^rusPcdAJI8`Kl@F~7H!3bB#2 zA15M2|G88)p5N(MiCIzd%dt(mP0H_H0Mt(^-wtWQ!Dxt*k$Uud2#8G^GSt;{`%r#Z6!VeKKnHD&*^{^+&>^dDgu#>%}kvW$|9`& zDFORQr-I122ZO*&$x~CHfjTc%5?Yb01RWXr&!GM7gMY>zohT<)JeOy9on~>NcP8a7 zAwS{jYP9h#i9NcfCOK!6ZAS9Q!V>Gi68F&j?Ptd%JV>{{0|d;Gzkeq10rWmD*8Cuq zC>CbwV%WAFbTKtt&D}y;&_ct@S}LA74W|S?hFaV&960d({n6jq=j#uikQ_TyjQs)S z0!G4of2b-lDXSrveD1|(TUcA#LPlyrTv{SpMm*@9%*Y_EZAoR`*NXN1VyyoeS76dG zgX{?T=ud@B#^4-UB$2yAlUkU2*9G^M3p#%>P<^L}^vl}N8iX$5{-1WdL6Oh!jbmme zCjts@PRVeR;Oz_aJ&u%<;(SnQqAshGmiJDwvN=hH=ihzf;o2cBLBvP4<@R0Y-}>R# zm>Wn43`vv^7K2FyG?!6N#YDbK6AVaM3Ga^@Hp7(4fZ*AOfJ+8^N`1@A$GsQXY^({M z&cjikjv6vMykEc`uAQB2O0?m~isxRv`Q4uT%O7N&fK#KfL<>U;=D)Yu?+NYv+;wB` zm5cQ%DpiB?DE#fwY-xE#g@2dU3sgZ5MnWv{;COgj85a1QP>CI7*@r>#xh4xO)r$JH z&(E<52{Sb!4&2;YJDT#=bVx{YtarOI>W+@m&GrQ));wo(%LKe`m)jZv57*+Npb3?g zYu87O@Cfji!J=yavGDK-fdbx$Jr|u8WpgN~*2UTa)nbEzOtLO{S*RpgnTg&plETjx zC{>UeTP4;S&2xZK!MnF_t&NQ}gpIbplfiu^;GeBC&l8-B8J^11{PP6-og(W$Nhf=n zi<_J80d-#?|Ft&Hop!yA%V!Ur>+2D82Aquzl$MqjI!Frq=+3%ed@9g+gbUHoggu!qz{J=D((4pMO`lF&r*yky?x6!Nr#7$CjtF zmTN75XaVAQ`s==YeFje{fv%e~salPv1{kD}R)$K|I!kkg{qXbOXSTQ0L`5%$!*OSi zsVP6c&n8ov{(idLamz)>$jjSaWBoO)z67YDJ+cH37wx+Jf>j7qQRyWXIt}qN4<8yD zdU$w{Nn>quJCZdJG>{CB5#684D?GFir2kSNgBwx*p8S^2{b}YX(M8(IsxSO)iHbvA z#oem)m#)rvoBYDBxx|ib(b1Kyk6YUb3B!prY-a1E-teASuF4F$?(LVCqHdN)uu-^d z$a4ApGVSM{=2ixgDsFACEKg4#`ModEXMvF3REJV~i-pD6Nk`A2rg{qC^dX_A#}DG= z)z#2Un=krUu1IDnVF{aMVq~-q$-=^1C12}wgwdwD8oi$TxD|RHYHzZ0I9J?P$_TdQ z^K?gbOfG{XA}rkDzVkINk0e#}v1^Q%nt6C|ut;Bs;*-j_?5{ZAM*~90#<*f-P+!3t2 zDBmsY`ifemqIh;&F4OLqu77G#nQ6ha+g5B>-DV3gBe2$orRpu$Za(KL#^l(TJRm+Vw5qHc+>V5^;PTX@015eUj85%% zlX0d`u&~!+G1jkE+2*EC*{(u^p;oMz>OnZYnZaj?@r&L6yni7;!vcR#+1CFwjoIM8|Ta=GXb-6y5m?RL>a6_BA4Dt^pr zxck)GE1ck$tri^-VHj&rSmB{nlCa{V<*2VYglFpx!g=jKlS&bSXtnditsj=Qi&z;NP!@g9-=z z>8G?<`C8TWd|qd53o_Hb)L=H>acF5KC+063ea zg#JXKj6bUYxrQyB$wxs>uB!O*5)d*8)B^`Z zC=TZ8(Moc<`~t-2KekX)rQT8ngX@p7Zinx{Ej8MH8>YF01{Rjk9#?4_o9Nzlf#fW{ zXAT}OcbEB@nJzRVwX-#!!H9Ox>u@UGHhv#^(su?>4AW)ihPxLxH#_|BdaDI8QoC!l zx9iofnS&MN448901EsqLYf0i797*1bBLsm^o_ zlu9laeTN!uu!fHbjpQ8G=R+j(J0On4*7Q$2917_PHKD7CFODyb;Zl5i zX-{JFD0;#OVsB0~e;T;qPR1OuTX8xaGN5ZUID&L)KoES0I#kJZVrqkX)kc+*c5?CQ z=>Xja;0MiqWurk(&-+hDM{XbLu_jVW~MaW3$IxyazG8JA5B)KPQ73{wU3Y`RWLs^ZkIDA@(lJNJmLKb^| zxE8FLkV>xjp;iqC4No0uMyiE%KM=HJ^$xaRX(K>&(slm@+5+JaNcXSz)TH8jkjv%^ z>m#kyZthR_-}jHY1mxMH!#|r?=wIf43NJ$y4n?9dR`B}SKdUXYS*O(~3w_b;o7LNT zcV{t3SV6Qw!0yPn>Bk5PT9fG9btEu|<*NCuclInlb$I6N)N7dbtM!{APh z!)Dn82bCb2=^9l?BtNRW1P&3VkBZM5GbeI;O8g2OVaSNoKPEiiN(HnuWB_jFaI&L5 zHa5433;Yc;T=&-A-o*1@F0<(kYlfgHgH8)1Dq@=T;JRO%0DgZ2W~JR*y7!#Ma$eg( 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 M07*qoM6N<$f>UknZU6uP literal 0 HcmV?d00001 diff --git a/docs/user/images/esql-viz-saved-search.png b/docs/user/images/esql-viz-saved-search.png new file mode 100644 index 0000000000000000000000000000000000000000..ee16bc00a213100db8f4a06c79d697b063dcb658 GIT binary patch literal 138865 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%0DYOw}SlV`o12dldoR<~9 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 M_2w1yrF&2RA2!r6y#N3J literal 0 HcmV?d00001 diff --git a/docs/user/images/esql-data-viz.png b/docs/user/images/esql-data-viz.png new file mode 100644 index 0000000000000000000000000000000000000000..ba3b6f8db686bb2c65d19f721e56440367dd6989 GIT binary patch literal 195489 zcmdSBbyr;9(k+Zj2p$sLf+T1lIE_0bSb|${fB}K){xhl~hGQK!+h9AWLAN0k6EK z)?Y(F_=X@SDfStnzn_lkMBY#BI-vPD5_{Qq^g=+C%a z;QzWCO(A6{{J-udt}65bhWTHY$aVk!=>r$0{oSCYW-n%T{nq$`f>$p+PS*08eV+cL zh5yI$-Fy`L*MiDzc6N65{*eeWJ&oVtbezx(`390|y)qm~1>bti4(=bV?mgvmPp408o z*GVrQ?;AYtuSe73i8CQkuKU})@>5+hA4J6OYe!GLc~X*+$#MAq`}4>#{OjY$RZ2=q z_xGz=<${wA$EwtH!ko8fr0VRJX+}p!w{klXy3TSD*KW!dJ#rPMp<^XdWJx?O3zCsa z|NB}e`2XLv$OSwlEhmZ?m-B|`417|Djq>C*qBEi>bGG-;Lg8}AooCD%Z^|*?4@ob0}l%3;RTsdZ&)w zz6^w8o?NwhY@4VGJ%(9-?5AfhSQrlGToc1yu@|0qUb)?z<_$uHG#oeCzTne37i+Y| zEVP1qOfv)`sO)|$YM#gQ+AYbNnB@8g2KKM_D0RYM21rOq8$09qyDSG9<@$X=xKx9E zYabm$;1*QzPZxU@rNa0Gt^&AJD7Z26$39og1H13CrJJl2KC#y2d5W5!;N{Y!m$G8uO( zMoWby_V>l*e?Is34;{?V0m)FIp`i3frZ<@kH{Wm?wwsWEOnx~XOevJs0=pNx*l*~Hn+$z^^4e2nm+X&KB z8Ex(+I;`6aq}OL!FE%cETIp1%L-`!!-7gLqYxlUK#pixtatKQNv}PjczS`j|jG+43 z3M=3_&xJ*+m1^;W;c=%#MY6>_2d1NIc|~6PyEZ*=0$3%-O7~DIr*St+;dExmi?{0X zR>ccU{W7=VvvfAMgGJuSVFh5cgVr~nB6V+at#9PV@~o6<3#mUd+lenBQqS4V)pU(% zG;R*+iAGRZkDTy8?7q&I{72>qVeIPyLxQ&Yjj7b9)4fjA zNH?{vCwVJsc5}dWdu>g9Q~Sk$>K`OYYs zXVYa5qp@wa7Os+9e5oj8dB>jGWkTx}_`l2SDnnK-59YUW{{xZ;6AH6K=ShS?`2*2v z@`+50arqwzMMT+C zMGa$d=tq5QcnWwXwCIE*cpEr#st5knbwc<_aq^&^mYkd%wcd+M3PI*0N4p7paraT9 zJd^J5S!4lLs`xkqb~5`F(N6{CB4Z>gQ`gg zAJY?2M8s#40t*o@3!*n0|8+b;`CJE~y}M@TJ-zYPexfj!c3%JF#!`#N)&`{?IC>zy z)S_?Nf;dW~M3@aPK!gm68jKq6jS;at`eA6k`C z9#&S3W$9A!i9`)JF+KF+CoiX#oMUP8?P<@x*O%AKs^h_qz{02D*#mZWW zP>{{ZXUT$lE$~)hMZ*@?tm8%kP?NOAqG3wnUEpswT;{{NN#>^T!2|;yyV>3C(hspz zB`2s@MAhdRwiqET?nBx{KbJ8L@L-h;Q$^#yRb)Sgi(!;07yj5^XqZ3v@#DqX4RB6O z<}WFj1{)X6f;vf|Sr%|K?9~1UGTueur#KxDy2sUF(x8@Z{rl$^DIm8W^TG<2OU-xG zD(uD^TH6Yko}@J1-&aqmsl%mn=&OY$2HD@}BKf|i<2w(Kl$25!OugEbee>_+6ASSs zTx+n!=+SGD*5mhHi}$?Wo~Jdpd;z0TthZY3JJEIUFQE`}*Y!pD5S-_AhppW1$8WGW zvh;xdJTWtq8tjNV=K+IyjTJfTyw$DAeZj1+?S6I0n8a>y(pwxuClrDYqmdRH5p>)r zsG07jZ5Ni1JebH!6_B~Bcl^+9N90d^H0={@(`%)f;z+c)aD!zlHL>v$=0M30+HklJ z=1))eD&9=u@p^f~W+Ppz{=>)B^jDceJ(Pfez>-azyqOhhoHGinPA(*bGZMycM>6Mr z9KEV7Btb4^4lJ|-ms}QYm(4tC42R`(Av;iOco;vbQa88wJgLy?cgn8B$u^w5`+RsE z&`a^8+^|QLtyQq#A(=Dk`A2w2ZV-moaB%g(R>+g#F)Bkrv(w7$EKywGAIK(abc}h@ z{CVfuF339Pj}8tz1wL#=B|V?83R1%`_kMhgq#B7!op=M4_#lR1r~zGU%o6HPET=2i z?O*BsZc_ZvsqFpPES>Ph-)Nur07^aPPC~EF$cR0WTI=|DeNLhE2 z^p|XbWIf!GT{=8NAS0)?{&6>Nq1SW~{q46XSQYEA6C6A@v+ozZg2^4fIk7XorBiG^ zUic}05#SqKUu-n?(?;g&Y-7tr;k0rB1=;%GbW>GY1J>V@ zB~W(MN)65NLc=97BXdB2RUid~_GL)~4avo!#KRYxaOIuOaGxc4JZAM5bMw*3yVXdy zxiKCs`F!4EK;b8iNW2fc2_9lg&R`OkoKbK)-)JD1qCyKCs6I}bv%?#`v6c7-C=wWW zii3L4-O`Ris;v#LxSG7~%5)nX)i9r1O_ymKTZ(WD5vekbVkOaOKP z8REY-_?1y?^Uf({b1sr_@;yon66!$*e+PAFnuyP6KCe2Sc{2?(E*^brAD3A} z;WEvQ^}(iwAH3z^)Lk!l(ri&VW{Hg5>`!Q1Myvj1y?ZbPJmI#ub@#(&={{iY?BqLv zKaKYu8vfmEHQsEaP0&Qy{BITi9`H}Adi`B@!REJ68Gu2B5HbSSa|`@1o2$0xQsuC2 zM&$A^TWwB&k52>35J~#>P4s|bCESu(5|JaHn`~DrSMzN{*`_fg@r^G82XA=k?Jk11 z4uF2w1JUge@DDeOc?pg;8W!A$v)e^wS|P9Pmc614u9NBLq`nn~PS0IY3HM{!2#a{X z%ma;8Vlu2r)xc!dY5ysoC)~i2ws~$A+O7h+;jlN~}7_r4MVb^hO zLk_HtiF6edv^#DNi{D=Ttga--c0x|IMiVQV`HgrzqOmh5dPI3=tTd>!hvK<@pdUIe zH>2}LU)&cG4##lMB%U~Wk(`fE&X2UTuZQ|X$c~Lk2xN*c0Mr?o0sS0(7)NP zB$(RwXI09C9Fo?&?VQV3$|!PXY7pgftnsq9uTWU!%gVGRWc;X-YBdOKj9$|f#A)9T z*ceD(6(wdDCSv>XN>&HG#LxZ2J5n0}+1c*-E-R%A=R{J!%jeaO`^mZiAjqwaN{9le z=l#}Nmx5}Ur>O#^S;x)!r=N;R$H(wUE2Lcrd|2;d&}sUq0>Is~P`4xIwHx5`*vyj# znr|HA=QKa!d_mbeJu9$ic!gKw&jU&MWb&GB)34!y^S1Oeruq3GfCCb>K&?s!@A7Y%Jn zNpk&(#dIA(QL%bWmv72%EO@g8I>+c05)}p#ZSvgCrd0D4k^5raXXHTi3uH0BF8pd} z)UW3-?k>LL$HaEmqmDjWY%=H$r(g$;vohtfw8KqU`Iu~C34fm1P^0{3Vf*7~ko$|w zPW=2vIy4Ms0LnNslZt&l?(}$($;xKX!0@s5k^^qc<$u1|;!)1;#)9YRez{MW-!g=@ zIz{nD$;m04z+>v+*XmO{bG_voCcp9gdt`Q!0?O3cV#W;dY; zKn_7yATcC1Kigt`$5{QXRTz9(a=tUZ92;c2-mMhhbzge@m$b^qi~6s5qJOf-YOR#CTHH&ukt+)S#5p7ppWORS^&q2T$Gl{n*WAUF zNVW~GF{^MhpTA6>1MOPKtz-L{vv!&(iIN$vhXLH%dghl_VjMPU+_S7RV}o%{oTRiZkU2TGhZR2D>h@)o((i* zpkchI(Rp?ZhvgaOnAPDUD@Sh430wg53#?j>K*P$imauS!tC9s>Zob&p3&D$Z{Oy1x z2BY1)?7+js@yY^?|68jI_E1fO;OI9)-U7@bK0M_BHXJxRV95#gE6*T);=%3c4&dACnf!|NSJqu;xIl^#Djilx zWQptk_(P%b`7o{DD7DMyQhlAR!F2X|yX6zv&W{-I;fy3B`hLDT4jz&H`TFIw33@!R z%i%(4`^As5QS3E*apdJj=lL{HB0%-L8Y<2e-fm7&7y21VeMRaApkBcdCI2y^p` z_9~Mh`XQZ~#02K)-i{z#3aO%3?E12MbrjZTLDb`=I_zLhta1aH0VNTeSkTQgt<#ZG z+Xv|)SeoeK>zwS3UYJa6V^ktRUcVb?YLs`jmVdg`!wyKo8IqEGVcS6Etvo@?-pFXJ z`T5<@_&Lu=GN7C!JSXK`aBQhB(YuQ*f-RmLE_(2L95#oO3#l`GE-9+5v|1gquviXH zsV=_2GtBg(6sgk_2_H=3?opQwaNeEx81kD@DP3KEsH~gzu;t6DQ4}u+KO#r9zF&8p z(d!bsWyGQn&}abR^N4a=P4zpxMi}M~dUJ@G%&t+cr-9@qXLH!VS>huj;s?QDKUGX` z(itPez9JTKY3^xnz}dxQ_m~*px%poC%VdW$$@GHggxu;VI_vdjs$J>u2A8gB=V92B}c^ zugg9_sx9?MesELeGCpcU#xDgB=N@|yq7nmAK7t6JrYOW;*V5yPOQnFp%G4~0%FG;7m zb*St%aS(ALuMf>;;ge?SvWu0tB@v7!*T_$4r;2DDa^hi`kxgMTemJQ$oW{?1q+9Yy z8iWtSrMl5_gL&WSxepxO3boKc+kBqH>W*gU0ez@`p~IruY)EbIJL7dD1n1~0h!=!v zRhyB~cKXZ3G#Qtj0yGP}!+3b9(Rtv6#kJ&MX2$v978~ps_4|xybsvAIJ+0oB*mWlO z1*xUpk(U%mMyNt%=<9Qn{@9N7k0wyx_rnfor+&YcG2M%>;APX=;=Ojr=+dCfj1=z3Ii zytxGmM}rO6%-0p$PrJ~42K9_caiRF<*Ae0q8aeNA$AaO*I^6`vKv#+#*pbEv z_?n4qI);ir-WV;l#(iVNe|lB4=!T3KRj+H-A*Te04dX71$%&n*dU1*18?s5Uf@{*-fJ&nU! zF^xO%Ht`@=i)jH_IxhGRbxA^U#8n)Dag1G0^jV{P={73uFvSK z@plVC>D|s=)h$TyS|6}ykdYHs!Z~Lz zw2Y1eA(6EeHg}PUHI0U!UeeaABx5Vx^+n3X{hUiAN~AI7nXNF)F+EEv4~DO-{E>)N z@Gz)ImC#)OIh1UwnD|B5Vx3{*M&ShOSzLhz$EgDC-8a10s$lOk{X=wrxs`DY8`kqj z@IPP_*c$OrU`bogOJ>hlZgv8rR-AWv;>Yu0KjMmTcSRn5i9I7?GnC6G6Sl|)sM9kz z^u4RM8m@m%Eg_4uAEVZhF!ILDS@6tVfWLol#p>%x{)6LRfsjonGv?>we8SLm_eD}J zOQu)sdZDkO)HQ%`@Q`q9KHQ4=@Om?7tIxtFY`i6*3l8ZV%sqRQoT53x*>VjJ;vrU9%< zzDr&$&oiJu79b?_*Rz<6?;sbCH4fFA)?WaUs&cj76J_OGxTYDz-u9@-`E{KVjFFWH z-ssN64#goGv^DO!E_$--}X9Xwih)}9VI3~^HcMFj`PH=MmJh>+Xz*jCT}tVLA#=MxYg zQeg?IzMNay;`{Nm`MFjQoq%k`7x@4o{vR-sl`=@hcJ{f0A(*f#*+}V@%KH%^x5w@m+7V7!WZs{J&Ifz!)5#v(M@k|hqB~dc zV=q~%jxz5?#MR4|=3lsvku@&(uk2aJR(`Ckd{Lt+c`dtFv?P7bGcu2LRiBz~0dArE zHLQ!P^sX+ZA>$pI?q*2ij!h=nBiYvxhAp>e%IF$b9PsY~B=b~(KCZQW7*}FVyI(I{ znb%vwAFP^}`p%s-sSMnOyIVWjUbD9>b)N(+A1`kc=5{%k&Cyj->w0XTX*k{)21`nI z2cPx+n{>&w5AovXAx3&DZsp&baTte;3;gKjpIBvH*UoR^TzX(=zuX*qydW+VJ2g z*zt>E4%t+I>41;@O^mSc8vpKh2OK4*k}F~*;%QmCBp*)024^}FaGX*1tl(+=kA6`EWQ7PO-%g-jl9FXSaV zx2ge6LbHrxE>p~}<|^$hu`9_e{-g=H&tqSxj*Z;)`^*vI;>u< zUwR5a?s9TrPrl)!mAP6gPRQ|>ugk41{pk2qBkDl^IRSuo#WmwUKaOu*B9Ak46CY3* zH8>s2sR4}dtxObU1W~IJ6jTTtDXf<)?-mXSlk2^`y#YEzs`faRr=So)%5Bmaj9+Cl z&!dK>;ad~g*48$d#{a&q#r~<<9M-sR91q@~soZV+j*MP(>FS(Lkt|}7Ln9H`xs!uK zA<+8`5qTqs%3IkB)nO+2DCV`y=Z5gFBi-BR@e~IE+`XExfh6{x%K*0z+f#*~7`Y>b zr7X1QDFj{OhtmZI+5@l>@NBk5b7U$XX7ZJ&4jvB1pQwP?77N6oD0&q3dGwrQL+B~Y zY>O$sS&{~bCX_UTjK^B}s%P13=tmxk6NR;QvVgOtbxY-qvfb-=D&v=??Jf37;UT#z zfP&=+6Op{95=pwE+V3Iy2QJ83GDwBNukAy?t>dN@xaPVgk|eXurm0g+ zfj1(!(e`?+ZH79{@_?p8afCF0LF>a8{f`0OOCspvyPk5yXM~jc9@Lc%foc}3x6}d5 zV9;e9;np>BO8Dm43zf6Uw53lSq=@(=_~PEe?z)&ZWgz3lRqPWG9oE z6U2eB^)Knawt{fSt@sF_cr{seovSF+ZAfL1y*BF`&I{MvQtevHv^%jcK^EF&y8IV{ zb6C!zf-B2UXChCZFvkHSz~>{7**h3bW4=O?V&3!_@q4pyrZ|#&R@cLY-NvVfgENjVi2FcTOJ zRGSXGyg6(-n&9rfBR(*Ax-dw{7w!-ENNizVqqo`~u({S1)}3AKsd5SQJ5?9o8?<^> zxgP0Xknw8`Cs&4%_HrwmL`sw`LdH z-UZHW@O}u~0P~^0qD(pN=JIyxL@xuLLjEk#CfB=zLA6EN=Iy7lSb|UXKiQf&f^f*b z;>$X)?EK6ar2yzV$B%l=kdzvmd9e@;wW6t5pmH=lVH_zWPj-G=-)6Cf#$wyga&Gv#$yFWqEdbRj-<}(#4^7dT$0a;NT(km^N=v!BX zkO{q&kcu*Fsvc-ycI1e)2z&{;xz4lGph5dhDJ7tRY(0nJztSxbCHaclvD;3WRLMj z1tw9S$g0y{Y20Jfp=)g8Z_ZXG)9CXE_el&VdCwF>*#7PjC+qC*(T~q8f*}LDj^~I) z#FRS}L{YhPfQb(~A?vh?Cvy}3{J0jz{+Uel{$aPcJpNr+1VS&M_3hmT!ijijMc+w3 zHfBYX8?@P`JT1L|shCaFn)JmMl}4AdkxPimQ0=W%sDNbdj6A6+x-o#HjjG}?nk`kC z-3GkcYQcCm^LyscT!4Tm%M;+onh<)t?hz?^gWTP?Dujo`?JeWMZMUQw(1Q*QhW|Z} z1-C!N1D+*1egPvNqDJ{lP+W+S;E?lWp7u0We;sGI8P9mkJ^X(1?QVVM#R59zAu;^T zD;|Gprf-Z#rI?!sO;=1#chwb1u_yzG|1OE4j9oxU)HG<8BvF7iibd^OL<9w|7m-_Ngm(b? zdO^jlOcX^DAZ!%lP2eFvP@xup4V0qc&i@4{g~?<y#D!NI}9g4KT##q*q;s#Qmxhg zIDe8ZWG+m!*q*?fjgNt!+~z{Ffv-;i4F^99;xf!JiMOD#)iJulCx-f2p` zf0rayKV>sF(Bg45BxiGidO}XU@owrXxd@`etnDDg63?i5v$wcb27R`yw7jum1II2H zu8BOj6P`$K#a65Jn+)Xfl2)(r!d}-y&}lz=2o84`y8#N^M;^YHU5cQmwPG&SpSn`u zD#+Pths?huW0-+V(_veuFPwNu>X^k(%xQnRJma(?;CjC1>nD1aHD1U>_Mv7|_!@t+ zBhBwUw{5f|y%J;M9%6G0SM02pM(-tiflpz1B$&Dh;6tY8u5h;WgprVCnvpecAh?}R zs@0gSXKvzM#XG@+^#1fgqLEcb$Oz)eIo=i`7*D9rUMRPTX$ERkI|%*p8vU8Tq^62= zwhYvro+-gkKR*q+i zNRW(pX`lVxqx?$ESO0ZfIzU}!F?`K!YcQo2z6ndLbO6Bej5#%Q5u1!h`Rbbt(8gX| zZ@!bj3$ln>u9QPGoV<9tI+g^J4p<@LEtKA@c}l4$8i_TLMkCUQ>)BRHUF> z{kGfI*$8Oxa|o=;!d8ZNg}w54NS}W`<)@T2FLdPdPjQK!Xtxsw$ym66p^Xg$ume$x z9Ru$NDX$9grFWj^?LO;%oAdQY@v`R|aup@o)l=%>zJGL_`fGL)agzj=2w>?W1BZ=l zc9Sv_cUMOPC9_}~k)@6iAFP*bF3e-}{&%>PZ@y2IQ5?HsteC-U;-7%7m};FTy#lGR zb`6K5tdeXnAZNNs@`xsTpVc;sg}>FS%N|U5uLL0Ja`DOR0}h7KSB=zoRIxfBS0v{@ zeB*nj%_aAz{g_kmPF9IiwyQK`1Ux!0GaJn1V_m(nDndC2zb(` z@`OCD_{-8~nvr8x7n`mk1BHb>ug&KhkW`!iZNgIT{aOel5^n*IV_ndD9jg7F+D0b2~bZOPyTz-&a=W# z=E-pk!R9b%;cN7?&NCMZYG_Y7td$-rk`NfN7y?fK42whau2gi}U;RNq7xeJ~{Ou#n zF;37c*5*Km;fCUOYtkppvN5!~`&J&A{o} zlaz1EG_3$+QFF5Z0!uKGmvhO_zMIeA8o~k}O!-ucK1XKyEvfcUt91S23Hg=(@xhZ1 z5w_%8ePGo?hseF(uE;t4aP~CdS?YnbYl`c;n2M4@BKBzZ9j00y zhy6YxUz7C*a#C0Q25ePo8Al5ZpQ=pyvb`l{6*Gh>Qq5IQSndNtrBAjF;97+LG~&fo z3Et|1g6f^2&8VzyyXwCz2v=WGZ5~$=!im;9mVl7hByDbh=_ylY<25N$K}3JT zk|!P6bLi_Kk+q}T#?bfDv^m)(C$V|4+^H?o0w@K5s5y7}{VyUqs^vgdIssHS%`$GX z&XVjdd1e(4ppD;eb)B4exjVThkJ1(7SQErZRpq4yl9@fpBG$jWw!kfS@Q-SRoWzD6 zR=6&EYqglAhg0y1S%s-au8ZV_z>}$x%5`HR(;H_Z&4W#nmH77N7QJ4wYyIq$22(bQ z5ieGmHUU1Ko>9C-L~%$+tSPITXA#(JXEDF+`sSvqp^b6vW~x*W1T_djG@2PPJZ!J4 z|KMOaKeV0d8))HdGg43}fJPHm*PRu$kD~6XUIY26RqI}=usRmZ+j%zcdZ;-@a`0*9 zaum~jDfN#TE@b-kLJNR&oR5|U9QXYIErBw?X1YICGkdw2@QD#dPj{aOxOpCL*02d^ zp}@h=di=`WE(quW49Npa({J0TZv8#scIrTX*{eSL2vO9HSDK69qz8%ym1V=VXf zc$Uk>F?2m^Z~=0A?&|FG6XJFrN&V*c-7+8 zmsVm|vf+?&$=c9~7TiLGJc^zIuW?W6ydQ3hydsWX$i0Q;wBlv0UPRtkk{~Uu{eA<@ zS*$F?KPte`!8y8HemHcMSMJtY4(9su5?1MkApXA7+Sq633Qg7zPTc-;T^KKa)CD2c zW`yTg5GA+htEumdYy5LqqX^WEWCQlV$>KZeIW7v4jcQ&Ah~x<42*yU}MR#t$7W4Q` zMsWnjaERi-ggMn9POaxMoJxf+TzdR0#a;5L|F<_*Zs%X3-2HHR8}$z7FJVGrH1<}T zc^AH|sHpGP!4cKpd2JV101lc2FvPdzb=@CzWD|bJA)G=`%>D9^kyX1c<_GtlT;JFxorfa)l3QDz05+-t zs6BDt{Q~(CnZZ-KoEE+B8a$4mI)9Ar{pBo&IN(`iw|st=O@a#LL#+As@MX_b#ktzO zTpoU62S%vGv7pCgTt)nZ=rysRZMMmvHPtGj;W3BnUlJaPjmPh}`N{Br&$#zcFUd60rs6w8 z;nZ|opPMqQXov&hw6qv}o2zfV=k5cK!{L5F)MHOUy~*bY+B;VC_S@$U5?E=@(fEKUzXLCBu7Mg%&T2YxrlKaJO zSLnR-3AqhAyT|!rZ_I18jMwZK!ySRxVHb(KKn7>|e%2k~g$i@=*XQ-Uy*OGHQKyq3 zhFHr>3JoN(7C+)RP|*H5(If#ll=Tl$t;)B*H6vF$f_g~iAtK|ipMCj+EIx3w+-5L| z2sQ*XIMr-P%*mtUnx2_MK=W&)OvR9nASEgnmU~lfywo>zr!*<@Z##gju4)Y=^4%^V zFx5RAhqp>u&21kv?jviWpC={bDbj=BH!$e6$` zE&v}g&+^I{aw?2iHyA4Kv`W19oJx(*ueJCZuVp5KJQTWLVQ zI?R70XxZ~_z4)co~fygO<9zC^V&ajtpw?P2Fx$=qLMff>BT zCL3+DP$Pwe9<%Z4jz z%YcdZFX{%oLm-Ujac~uAn-X0jP|WZ;R%5VEW-S0wJn}+1u>KtgOjEPw?!R`qvDnE1 zm5`L8-11m`#Is88JOjC%zQMhD$*Nt@DE6ZJW)8S%)?T3lw=6@!b1KKq1bQ0FCY=;-L362qykO_l@LoW-ynhhfYYopJ`Hl4 zh~{YATYPv8+1>$w@*H<*P9g%ak+-hUAk*uQZv?ZcR)P`kd9#sL(%h{Hez)K?d;z3r z)nawli)D-!pN-xaJ~w(I4!h-n2#-IOVC;YBB}Rw>&>jSo_TJpS@&D-$9)A$@7-qWv zLxzXMGi<=BQdDv)dvJ~~+qTj#IfXZGoW& zvU^0Dw7Vt_!$4FtU);^A9HQ|^mj#-#D~rFpAC%^QD+~`yNZUGZ{4h7 zt?~E2pnTNjv7IaFWC(W?#D7DRNYDwcK^)H1YjBKs!KB(=w>(O<$*i7mAm}n%hG}O1 zvZ@|ll|EVY$-qsHtS3eA1f&-@$n@a~PR+@w3V70}8yx3F6adYGg8k2UIz+z&LSm!e zruvY8*Wpe=rP5EaO;+NAmuNqH4@`}i+=gqZHUY-3A42s9GvnCiOGmR!u(}UGi`>{~ zNfwxPV)^RaF*j2n2Tn?56P`SX02-N^&wY43j_qa}7b|f`DQEmm*vbLiFoW=I{nh?0 zPAax^6s5W1OFQbwJd~6BF!omE)tXf|D=v%;cVeqFic$|=iNQ`G-2fYznrCS-6fa)n zF69ia@sCOD^DGyuMfWduF+h&-x1GyxU5-|u+onr!R^yZ*wd z^>6N8WHK~m+?qdkxEV_w$4AGIzt;W?z+~Q|&F+UUZWnXauK%9Xi=L1SC4xD$ z0G^PE61C;n4BO?Oa)8;+<@0Jh>(_?|)4`dONTYCOKa9Y$VPGf%{$|PRuW$0%$w{?D z58JLp6F)AapKSD7Fb>g=nVp3hAUyxvoV_BK=<67${RFq&&7lQNqGMB9Hc0cVAY3~BdI zFMBhbG`o25JMY%OfC`oNuNj@xw%|N+A2ua*e*2Z^9`We7<7dqJRV7l83p5MkczmOZ zbhlfOAbU!s(qs~@Z)k{5> zDKN8ssomn9-UV0*#mX!%CNlEhAn)$K=%#9WsBwtf6mF%1zMkNPmb>gRPW>WzZ=M?0 zl{(E~*M)01|62d6F|4{WK@%t`5^2H?YyLN}d|$8wfOAXOZjvtGJi$NR?G|;`vl_F| z05*4K5Ey>R8wPKCdN@kZs7nD#bRph0;Go7>elyS+LO6NRST8UL#L904-Bw;KQLjEpClf_A=)&`B%g_6=|Ae(kGAh}w-_71XGE;kS%dP(6c z3|eRYT#(s~#bfayg|q9__7F&(b)7#k&wi{uBZvE86ZPUE3HN``K?7QS`G*m*@&V6? zRAZhg%hVvwp~I`1iUDuJ&28y|dvL`;*^@}XKlULy?l_~aGeFZX!mImAF1h?XrD`A; zKPSPwAm;B>A28QL@4Doh$Yti2iDg*%p+SDvdTfhzzW3XA36pmJKGjN9?x3ip3>gqE zHdb6#hjc;XkMUG&noO)0Og}+iR!e69cQ}c#XZ$l8EW`-_!v6Js@w#!NBNF(cp~z#9 zzGBmlC7>VBE*m|VKN?fG!_nK=`dI9mLl{B?L)>)$TvJ-DMtRT4xjqB#3KPA5{Ezo_h$3EnAjEz z`Odsurm<7N2Q9~j$uJf@@m?xwK!eW&PHVzs6^`wPZgb zkIROdhdbPY%Duu~%vCHSnn-N>Ig|t~GIru1m-TtFHU247>jDRXD{>V{pgUjC%pQRP zO!@tCAj}KCWD!Rp%30M~EOr%Xa6kCbJbn1Ra@R?nuf%CK)NylHE$rPkFfP;|z;RF$ zX_&JVnov9l*jPaykfETYd_(q{OECMrn;!rZ{>o(S%wwe3NA}6nx7~#PD^p$be-#4x zihvnR2cjqcOl(}^dw`F*@VlLw#0CTU%Gc>KJ>vkw44c4j@!^j$=iI<3%wt$37ERe+@MN$fp!rfPZgWv-A?1~BC zL&g*L-r?Ssb2VT0lfT(V^`gu?|9)$@j?(@}#;a#Cn5+_z_M{7pybS0);nfQNa|HaO zf91*oxfgo4>_ZI(e0Ed5g5_y0|pEPpN-=A{8UWMYESP$2f!nO zF$tBO18etO-GN|25B#@SM-9ikSkdR=w+)VP*Aj`eTa&)RP6#Ax=NBQ2gKclZ%HmCe z)p3mH-(jWwYJS{B-4OUM5r4jXrwZCGc{yM}*km}(4VZVv;dj~hpTqceLr6eS)Lc3A z)#=O>ScyI0MC9yly*NQ^bKal$Sf<}%#>_OsVY=(o^N}N==3=KkAWWY6L&>7gv8dNR z@0tR@w{78NLj6{=x;QyQ$sF_RIi#_GmmU=8>T*J0s;9lfNn{$s!?+H%17WdQT76ul zrDweu!H#(cjE2}Y1^!j-=3MVCZEEEP()4*tv@|~r_7qYt1G7}c<_X0^Z}1iY37(zT zcIh)2YTB?z&)jIXnhqv*?XW38_f9}knr`{nxHHt?l@%KB8075o0p0DMMHMMSx;M36 z?v8v~4iGjgtF@}l0C&R*9~!%9aLVnB-xq8pbI7SUdB7F8#r+}|5JXgSq_K$z$g}BC zoFKe=`Xh2Q(ANf#fpu++-zUJ0$i=o@AzbUc!HZI%FJb^$jMoGR5}wlg>OfF*NCTx2i&$Wf=eSyMmz-cSOxBVN_yv3=+mi9C+w zwJSjVoBV!%tKJzpK=sHJl8AUJ|IYa#Ls)MT%WW-#SVWTw@fOi5mkR&ey+io7$2<6n zoNPSk`SZB?qqQsBe>7*jSW;PsHU2V*nM1&zBiB86*-p^eVK$@_(zc^3;klsQw#wKO zb$2`hv{2&61)L-?Ij(0Sow_*#0|JVz06)9T64oo$1ae@AeQX!)kKu8&A&{rIH;E!} z-M1Q7bd@2aspGqs4V`BIwqjzJID2p&h<=+|V>7LyUGKcah4|g(>QIO>>MaG(*^zcq z;t!`u%h-p**?S@>rVlUto>PK8xYuGq;0&1>v7R zN@~}}j5Koz5K3ZVx{tLtrW&@^TLqd8jU`%5if&(b;37Ff=iXweEP8bdvFf}AL@*WH zVBX{mJh~yb?aK?^uWB<7Eq9lK&mvt)bQ}5|z~bDjM}Vi^c%G}$tQ(-32<%V5%pITY zi`}K1`qM9${u=}^gSN=TEmrOMDdhE5ub|;dbC5^Bv&{pG+RtWsk;T6g`M|-C`6fDj zu#SB~E}FbMS{=1s4H&a|fO0r#OYVUeuU@8~Q|m=yV-u1}d9%r``B}hezldjZrc~%1 z#>)GelQ0Z!Ghi~Zr`l^Jlcp|4Py$L1!pRVp1S&`MlQ z$HK?YE*YiH6#qOKMgBYayQXfNw->#y#ESsFkQzMg7&Igve+Wk3Eost=HGWY8KoXMV zH0jOkUoC{-yfeU)CU0jg6T4BtO+Mt-t1^+>l#6iXn?Syr8bHDYV~m&`?TzOvF^`hp ztcE*4ie3X8u-@vZM63Fl_-a#o>Kgy=>KEqG#)Js{zGr_{V*lG{Qg1028 z)C(;>5Uh!V$DDVV&m*I8sf2LnmHrW%W3^>f00YY$kZzi57ci;tB38YuUwYy*5H1by zgXgh^o%eYE{)>YF^q}LPcox**vzRCV*u+;{+{d;=X#@T-$E^DPh!ip8?d*3K84TT; zBz^#*0EtUxLmg)nh1zn((}htC-&Y=(c@TOz5)PQ!WKzHQNm*DV6J-1O`l4jB(!Lfw zhh$z<7h~gHXGTU9Fe*V40pBb?CCxW)e`VVR3}1)vk@GSTm-C&lo>|ZPOcfW150CZ4 zgnF;v|6%Vv!aZc(uy7K(x*9Ym#rg7hXJpddx*U8HyE9Ym@~QF=#u?+`jDARsOD zKteGf2`#h`kWjLx&-Xs>bH4ZN>-;)<|N8O^7G$lt)|%zM$34cFpu}?{t;4dyC&|uz zumm>&;6bBlqs!Rl#dkq~M0r%|MOJkT)b>h2vIlk)y|zGcr%5L=Y{~{0E1DldJ?t*Z zrep(hGX@TdXJX$FgoXCwQX`;Nw%d@6xCxf02u_sqlHJl#-n(;uK`SegnFuNwJlyU~E#9 zKZ3q(7bVYI88mtN2M+;i#yunuY1R$7$N~6Xg z4Ez}Aa~ja5;(h4GqAy0QCO!S9?;c+9KO3-U^`a~1qG-F}L4S826q<~r$bZ%#Is*ea z44bMerYemLu6#}r7b~U=IRv@nbKLrrR=;ON*U{dJ1`-;6ohCOo_zvuRIfi2BGuu;p zk^xIlK(3T6S*FbpBvX6+iwKHQ%@ix?{8QWeP^M6Q!s~4}9pl1T3&mDXp-OuGo9~gFfnMet>A|B? z6hFrL@a-UlCI0$==qc5c^pwG3I(UCmjrwJWSP#WhoE?iXD}4tSbAYh@jJlEV+n-Nv zNijM^s_%+8F}e#bBCLUqrQZJa6KjvCTPHDRCN*#5HwB9iLD^R`gyx+-Bkb1UUH=yAlsj(KlJax!F(grGv>zSDV1-I2u z#`2v#_oe(2$4m8~M8m-X%g`1E;fGv!tQ+^`9?OifXjh<8F#Y`-u;ye3FH5olaUO%5 zRku{KSIh}|*q~T~Fl@0iIa&o&TyME{n(lUhu^E6rJ}#>Us_cih`y%dVetIAQmj|iyX2BkIHSD<3_?#TNj_)o6Z z#EIJRD(mNgaif+Yv2v&4T-Pq3tR?p4agEYa$SEh+kauG~E{USBU?4QK=rrQ|Si)l>RG53wT3{;SoW`7JlSQY9?X zIME%amlHlY_P}~vpE^EiaZWJBb^xA>!M!zIz4s%ALt$~`hOV`0Z}L(tDK zy2SowJ)g5(6+i8ZL|5YoS(C&#I z{Yi0*$11#{DBpGF^=m5=e{6ec(R|wF_X1MYg0OF&uJ<)Xq!uI7wRSyI^Zz9%8u9UozymVNDk;|<2)0sB!&Jq#w&mJ7|2 zEW4YP@@*#6D^lWyEyIdHu=l%TBrVA=yhzs#Hi~Dvo?dy&ozbu7Se-J8qQ&I=rU$P% zQwJ*QlXP1?jICiIb8y@FDRV!mJBD5~_o-ptmq>}4=W0-L2efVQ?e)l$(E}eTuK*Uw z0VhYZyi8G7g~YyF#cXhT7_*5z=g!le&~Q=srK3EQ*zo5Z-UoICFBnSY zA%{G44i$Z#N`KwPH?Wt@<3o4fiy_6#(%*CwjprDmeCbG+znx@-`?Bx^C`zXY-}@5% z$YWQ$mp(uk{h8`yZ?Nj-{^M6$_;97iQ1O4tHqIZ^BG4kxBfuO6bXN|GKDCSlpqcE4 zIEb5nkJM6H4MOwCHWUv2)MuY2Wy<|X#Zz+8REH^(^)9Ogd_Og2VUSfZT8o7V^HmW7 za$b=vJ&dZvAr_nLIPWtXAQC=hH>&&LD(Wpv?DxZ&D#8>=5sJtBHo&5S0RwsaC zitacq#`OdGC*UQ%Dt>!KnMOh|W1h@uE`-7a#MwE>75#Qgqx(<5*puToA1b`alp#RA zd*KGzTcM`R*P&A1FCQjV!1cEZ=z8_47W`L!#5XM%E!X?89HdnV({@E3ZcgcZcuPVZ z&A-gL4V7lnlC=ejV}?HFp+v8kx(|ao<+jb*jeu8HhFviFz;KSK@CHqnO_UV&GG3eXZ;z8PqN|3$OCVU5?hm{}AXn>e^!Cf!j)rx>DuuWF8eB++} zQdOguqq{pE<4V4vh-o2Bq`01Q%io@!PdE0FaJ6@l>O8R{?7iQm$o_#uJA0TjSZ>a} zUw(y|-Tq}n9{etbR1HV>_)p?q%u2R3*Ibqr8v$1@Gz82APwfgHhGe=hH^9wir<|^; zKtK%e0#XOlD&l`2BD4H+^vXbuse}-G-0|dLFzl%N>s#-$yZAb<>_FAg#Z?2R1|656 z=4i3izDqZj9E|(g;>814_I~RnK*)3#CI202$VS9Qph+4GDij>t!>Vf68xnQwKx#t| z#R}1dkVK@A1Y-vBNWS`xWx`BARm}oW0hOMFtG^>prQrR!M(T9H(ybs8Mi$mg31t}o z)Sdn@0dgz9MjM225)V+8VP-fCAdWg+?|B2Bd}4UyQolUA-Czrg2kdvwZJN_={x+i> zXTm6wb-dEw3Q=9t;;*_Z@tE6Cp7ia_#Leqn%p=6%O=%QMTv;1k=G_Xz_JbeKLZ1nF zZk?a_!LOSs5{XjS15zah&=sZqU;eRse)!o!>=!3&IFu%aY6%6J-$;nM}Q^4Ne zrC(9Jb4HfgqCcL(6R&-!U4bZBzKFo9@kK?Y z(Yk)M1@z=F$#borepoh^mo6|qpl$IuCHyltvGgeRxH{2<@OkZIiF+xN!P znP;%>P}zhG0jmx(5}$|@Fjlm4QiUDhxk8sfKBJ#mWL%){5ZjXu<;dzJ?8sO`v)D|U zZK!p^q>LJDqZOB8xIo9b09dY&?uj|@wkf2LJRhU4QX~KZ9Vf`{rtz*D1>{6i8C`ub@Wpq;w4} zeDRKwJ{){Ad>tC$aCjBjMU-%QTBbi5wF>i%vNsK>&So`j`Kf) zsWBnZLVzHbFkI3ry?pzPMOelppyXQlcD59{p^o|r zGM6X6af8WKr6ioFwz#ghh2k+kNJvOr_GfTUB@V-oUYErw-nW^rxj_6Q1oNHaZ$r=% zY*bzVLigi2lR@6Ry{mu4Cgjs3QI_0GzBj*Qo}@^S`u{lkCr?@W>ZBkMo^lRJlBI9? zd5lDgF;l`@2^iGb{phx)NUXJlvi{Lh171@JMTe*h%Jn+T{;?;3`I;;-V$ZSd?Ih+x zBnp3&ow*!eiGwm*J(O#1TG&f>b?+DexU#_OL+z_vbJ6>>IgY9}QGe=N`{W~oxJiST z!V#ntM0H~d-QYw)#K-lAfoq)M?QHhnqELkGfB@0WR*9EC3&idJ#=Yr>e1I*=1z;hU6jQ#A=w;_uTKwhFLeVbQd~~zeuQ^ z0*OX{@}ujB7tc6}knl&_7v8h}8a6zSj`H^xbu3D!uVb6s@54AHznc#TG&} zIg&gpHOtEgK_eR?mtR9U0j40OdmSDs-nEDoQ=!RQNMFFqy}|V!V7Sj(Ju>Mfv*&4^ zLg>jBpxGJ{mv>_9C!0bG=vBjpDK8L{zP#%y`v;7oc+Wt`!u;MFo}6_@=;Bwd-zTBh zAP%vvqjW-!NzA5KEqHW`Q{2|ajl9361;U2Ga=r%)&5RhDf37{aUdLjD%q`#s#{o3q zW!arK>(?#}{)cRI`Z77=D+ip#c?8_v+JFSDQ8=P*)}L6s4x-{65V?Q>G^-%_)o^vf z>YZ3iw^q9w0B|nf(Gj`&#jY{SHvWTXrttNbm(v8D?z5?8xN-G72=$8bF{8XN!Vf0Z zP(?bin{VzwXD+_4o1XtX2jD3xpvs-C;bAV9_Mv*Mx_)bv4X`bK95jD-ji3V}laD$Pe^_7x;}%?+BELf7)Pdgk%KI%K zDb+Obq0Rg{GRi07)U1b{b^%TRjvMuauQGzXOA~~q%My#A*!BPjgEUz}hGDB{$ew6MtaCF<$+0;;92Z$aJ{t#%0O}DmMs<(?a9B zpmJcm2Ox>Wwxg>C1NjDjJSc}|U1RLD|8U#`JDrycuip{T%#_?-bN!l`6I*8q<-v%# zC4r2r%qm(uPF4c;jn?B}Q*Uj;^PP_)LQimcD!_4VsC zVzN>XOrKeVIK}$VadWI0pKsF6=5F2 zDO`l>sW$xrsv`U@`y!&=>-BrKo;qw%mm>5C1g!RA=$VtCnM@ff8QuL_ z@Qy*Sm~RJT`iNaP}?8v3!&dlp$QXoe)L!i$WBB==ItBG7+xA}3Wph5rt%%X2m^J&JmBa@epT zZ2p~`i?Hoy4$B=cYRONu5k3~Q0;51r8gvV0n8(>Qt&uV)U7 zUuSkT`=9t4wYlk8+L%=WE#@+QAwdY?4GPcygv=wpqQr=!?_LAxcxl}UKlGnWYT4p}8o zudMhurGdEcr)NZPgwR8qJx#3#Ma^V#;+7+6%5ccWy^R3FzZ^Q~V&;ae0Om|2R2oGi zZd_|VXu*+ejE1|@l|pJSKySOYpGBa z$8+$ultLkf*&rdA9%@$~C)%v%JHLg4Nx1+8uXf%##97>Wbo6P1v){YR__Ec;y5bRFaRnI+%Fl}bwdDPFM?=*2 zpzUVcvRPkDwz{b&%l=%uuUaPIXmeKJzG_!6DU*LY(bH=rP;y3z1iA2-{!+mW^MT2= z@v6lqYx9krH!p(nH%GdV!^Gg)E|VNtXKsvpdOwTzC{i|i@*MOgFAj<G=dg z&l1-KmNU7Tc27b_%Wa!aI_jg8$_#G76c@&3GSB@|QRm%nZsxAs1W17`RRv)9xFP zkg(A2%8vn6PcBM;Q5vbZ!Q!W!(d} zE~rRu{Odvun}~?6_eH0?KO8YK{tlvk52XWg+q?CTR}HB0$y(Ld476E2qhHKuA-1_BdHX zxZEG5B9VP<5}B`EB<4;BcNCKKItG$rgpaX_($d)^>BhUFFUGc*(OwSx!45Lp;yZbn z`Fn`!Ys~6YYZWAmT(i%9pGibl0aWp@D85gC|18?h0B<_n50A1x{ZsmBkefC zi+?(QN$5RlsK(9TO{6)Oj8iGjv&6_%14D%q13HZJ^CmDlP^#alzfPgX zY#$mLI!IZ;yTc!*PUK`#C-N+gY@Zr`URD)2E7PhG1qCUk^xO$f77%)LPy084u-Seo z#R%pN@jqp%H@W&e4PpZ)h8L0w+0eP&ygX#DY&i%q|50mE^BtJa*QTV}^oYE%w6qNS zbqd4AKv##P1oBeH`e~2m4&5b_AEWIJeK8GCIMiqHmOMgZzkt}cSjwa+r4=8qGybB+ z&T(n*9_-!4)bP^o(uqSK!jFh8D_2;9S1_8OTBV$`H$SnPF& z@LV+RMC+J_PB)hT+!x2P{P0IFNZrs+bvwZY9_l zwYWjY_d8+i@RP0rJCDs@mk_?IQ5dPW0~5_mVgVznS?$(nUc&}#a%GqWh3qAn7AC>| zmC0FUkNqBRHE1Q#LtnD6#nV3Mv83;EFAs4Cu=M}y^NVQ6_1rnHpPy>D$*Qb5n_UbCID8&!1Mb;eNBz542w{f?;k%Q75qmrCu_D*WBTCktOCE}wgM`KCN8pFxss=+(}& zm6M<3go7Jk#`KZJ2ln>|n`d3KupFD@1<@iTm4oAoep1U^oe$MT%Yq?@B9kxYpcto( zQquc47M3Jp{g-91%K3U=fR0$NnpPp_=~qfLE^v1O+rBIh8`xi8S}aF8%B5?^=nm^ykho>Y9Ht?(mx( zUIeR%6{kEi^+nbOX6%<#H2Kw1Pkb?%wZUuXFH;D4Qt@qcqVMU*!vmTZPg#Xa_f1xIr`r@6gS1_E%-{d=Pm&^gR2FZ=3&+tl z=4#`NoYnrb;qNYA`OicDGbaAqEcZXd{r}2v-^-M~G~a|{YjrzK%53+UP&JWRts6`I z;+iv3vNeC*eMhY{%Javt^w|$y6M|XsVB^h$L*{3F{v7n3Bg@7vTnUF$8#$YNCXHC? z%72+Z?G<06u9_|2f}I}XDhvXn&h|%i97{S~mt#G);5^%E^2d}U^J;6E)j;&Li&{TTdNibx06VWlJUv#r$z89nV)<~7Tg+=t{)OM&qX1(OgmV`+u#RN4*7ffo>Ci!$>a3`lv<7R_k=w$iadD46{Bw z=UypT>JLq|gKRxp5@Ir6`yOl@vv#rg@FQi|W>4xz$o41=q66K8ArWH?kXt|W4ERFSdki%`JbP6S zeWxxnwf=>tuM+-4S$NxqO6<>S4nA{&>$Qe$2a1S*JzH5can5^Y(slK9T*lAdX7cmT z=SI*nVpS&(N6mVVVkFt@56?6_e)ouq2jK4xe?qDpHwpZeR2C}7zRL0Y>^uHcXo?j7DT zy;U!;l9$zHKgXjfqfFhbOtb#F`J4<85{{U}1qHwlLKl(1MhF^j<-gX~{Dxrr;`s;|{*^;2u>f2Kq`^es?&?5xgS)VQg9IBB0i zb~xLURnV2YlO@x;rqWZ;e>%}Q^(RfZKIz$-Yr=64h6R0g3R-;lKYDk>hO}hZ^Tq$y zb+%4!AsV*x8AN0U4q9)ZME@G^0~L=Ck3MKnJD?ZhK@NjkRm0i81o{2eD)zy!Z-8hv zTC(rIqUm=Mtk{#)P`WFb)FD@E6L?qz$g9KgAx|L%`%Bv|?! zMAyGse*O|l)Q19`!ypBHsP=aU;secHh5Rco`10g`-=2ri8!bHlN1CHWF@#OL%!g>p zMBAU>DX*l_Nx=@s+_yp>LbtQQW&gY$bz^YTqu|c|nkt?IoyXEkMsSj#yw`(1PCIWg z`gUM*FS@7HAv$9#UHLTChp>4?yfd{9iVg$or&kuA`^IL~&C-$0wyA_ucKE7?{(n3{ zO__(#Y=+zN80wNZmA!P8b9B znSgk12N-m`X#hK6IRQ8Mx?Re`8DL_|Bj}!pnf3k3YuH4!j;aX3-QaxI0&MY(fH0{g za_b`yHJJkHcw=Vk!A$LBVp1GG6!FRiH6#jovqCHWcBk5?!%xuEC}dAA+Y>7xxSviP zF15eOVCA4XdIDbX*w(d$Mq}rlS9q6+#*Es5?rFV-&l{AGUFK)o#7Rs|*2)K2hn~b@ zP7Bn(jQ<|fUz%X3quhD4J-jep$bI?w_6vp1*ypSxMNVQX`b`^#U5AHqT(W%udtvd@ z7F&#V{5Bs2Nokw*-|-BiBx>Vc95y4M_+q>$V#KRRKfBD&GQ>5j`A9Phl5Wa`^s=&7 z<&tv{#-E~}U-}^tSZotXswBCYO==u46EJ0efL@RQh0*+5w$jkEQR_6x$+s=1YNs{_ zCb+`cMV&i#f!Z!11cs?YI~7RhnRSAH^U&%(G{Dd*^0XBBms6pl6Wsn}sfHYhAH(6T@+y00a02dbm)HugJoX4QzcApzm z-GWBE14Rx2{CM06-We8}H)wf3b)ls||NKJcP}#meE3sxSco8z!I)tI6wA zodFx%IUHuSdz}1!E<93;=o~=5W(Upy9xZOs*jmEA?*n@~#bUFTfFjjwKnbhvi;!fRy>Scs47K}?@-f1mI zf9Wy_H^Q3KSUVfTmBZ|i zGeHkp>ML}K-Qp7Xo^?$OT|Pbb$Tq#@z=y0zQhEf{L^_TGVphv>0ghIOEHU@qSQ%*R z=DJVY;|R=tYF&r12kbE$zAX*S-^*4mZHm$G(964$j;UBEIq=9{(RSRiEfcg2cF*pF zDU?gZEufj|4pJoHmRXYXrZQa+7cN|l1|;i1>Tr^jrXXN11JihnJYsRM+v}yA#!MVu zid&$zR(E%sg}&k;e>kT*#6_}{bml5slv^fspPrhl@*m~n}a2W~86 zyC5+qSn|G`*x-SQO#!~5KIr2i8#h)@Iq!Q>$OuXt*+ghPb31M?1u9;M0 z?Ys6o1xayN%fm1vTVg+XOOu&1o2XchVU%;#*3P4!Hlf4*)NyGD^S&12f7n9 z+E%|g5dk}HT^(0z_XZ_kgBv48K6)FFOo#RQ1p3aer`-9;8(caS4QhDO8o<0XXrRSa zSZu@yS&xz!G&*V&^7UGv9Ofb2Ks{swTr+CKaA{3Bh&&zXbOFyCy7Oc1dw1LKFgvJf739-+bM$E(Ez`ZVauDA3|%#jv$c|@G)Buj1|Gl%Ca*$eMlI7vrNRd5VXT(KZNxZmB-UsUd}U}j^%@dBVo=?)UEu@X^_7`QGf!zkw<5o^Wny|%X%6xZ zNEY7+%4XV5=4;Kso)}cWwkd|DZf_V{+f;yK+548y(rjoQqc$=f0;;}hNdjt@6xMMlIztjsc7R4PoDlcwuD z1C>)Hp@Y1T^7ZH#6^y;dc-`ygA~E6(q%B1r+Y5Hx3Q;veISylI0={R~pLz~3ttT|4 z*ov2}pJA6s%}3u6OS(WmqcYZlA!VJ>+zdbrpNx& zo9$(vcjARJs+ly?R~ZhI;u|0MWoxX>i4VbTzT@-tC!QNP96$uF49+>-x`#cv#uQUu zH(i}SPN!UV8I_&Hak4IcuyAbQ zKTmzF(*hWJJ5R`dwi)wZ(e&}Ii1-e{w7@@xM$RaQew+zMHbsV?O6+Qy{#k<^e)aEO zxiNtUc37d>wVLvL+g(*{W=`!!!^qkMFq9*0}Wlf)VnLn0lMFe z1M8aF)mM4S3@a)tjO!LO)D&X{&b%z1bP{B|k5BWUh&pJeWBdc?GhJS;A3MoJU$7I- zwokfhC$$)zOZz3|Y3yidHu>d2khJd2_d)D_t5 z2L2M=KsY_?YDmxwH$T^Tfbu<!mUn8XLKKHuO>EP z+HYF;QFL7kUD(Icig<<4sq7-g1(_t=@?jsOLu!4_3WVF|7z0Z`CCe=rR?JhxZt-zC zXxRL=5L!!a*det3&c6F0u%xdn3AOAVr@teV@2fRJJ-r|_a$VOV^2C?OO{ zUnh*-QV`=yEQ9N%B7u@YN%w*Az#HI-B-2)=jz9`7t`Y-g)C!sIq%-bGXPfHe z7_DRA%U;MsW|3ed6uJR(YFQ)T4esBJ5iMlP_MXjVa_HK=NE#67D7R7>#8lOZ%Yx+z z%XwEBbc&ib7mo)ha_``={mXp%Kgc;}#tXP^CJmNIIiZ^ItxoPHwghee+IE^V2YUQx zBg<_F%OE+T$-VD%apBs5_eK0NkEKCsP?=x*$zv#6m9%MWW13x{fk%}8Zr)3p{gu%w zLaU1zCYwvG+S8Y>bwhMIQLj>Fb+9auuu^@RJ(+IF=V@&2Q0R0hvpTjSt;|u{xzMC? zX8aS4D>7&lZKX`&u>Lpv@x5ILY8SKcI?%{Ap~z(`RlZ0Cb@cB3vB<(S_?YRid zDL`T&K;0}3m*(k-I_i1rw|F)4u!>kPz3D#kI75l=eB_~O178xYO{f@e^@oiZHg4BW zfO@!2A%aT6H z$_#ezy*fb)z!>W9O5QfeQN{YFuKtNDyCrm5)Z`x#_F%Dy?oMBLbe;T1jXFVcDXt&f zn)+|sFY4UOHNG?0G^V@}%QaQID7lwG7t$A7l&bDS(+-@QKxhayrR4t@ex2^th*j$F zl?_1O`EIg0xIZeAR>}cz!*kD{%4nX{j-9S(f^9<}LBR@5Iec|){ncX~?SRCnSHvqh zPc`{*2PnD=s`B zNFIC<>1NbuTBlWE42@NLnHg!GQ1b<|PWyvdRUm-33Bdn9?Z@lE23|mwj3ZfFg)HB{ zpkkq0pf4%>-om_JBz~4ju&`b&v2KV@-Z6#&6s?18CSFOr(s)oytj5mZRnAyc(5@!l zu^HtjFBC&7@NN6TJh^n>>dV-UDB3>w*3#STez*8aT62F{u(uyrI;#aPmO;-MRKNW&Y*IZ#`dg>lLo=J|M#-~R6?rX{L=;tR zyurs>u{X8+$G-$EnEGRmg^|B+eWsH$tqH)fYlrfOl z_3BW%-|fZ_*@jkrGuJX!MKxW5OkTzFL0%!xvsY?~j7ATn^PjEA@^v~$AFr?;cr=aJ z^3_I4>4@@~)buX+SJgKTw-EDx+Ai|0+$i>~F>I2eoxHoOjwxSHEsTrc&DJP6!>x(; zZy&z0;B028Y$-T??hpabjOBt2e6o3ZSiPRMm5lE4(Raj0C8!h7K2%jNbZEY56cu^- zR=BrG&ewKJachkszCIX6zyVQtUSyj2(^V$e4jz^x7t4(xagZyiy;X6tvz>ilm*=Z; zfi8E@@WN3RmD9) zqzO3XB$A`|Uo=%sL?K~qZMV4%(@bYs6=RQ_&ZYL>;)2!JEVFqVNbpSR(2TWoN>>_+ zOMLXmPyRR}cdS0J>lLp@Nd8elB*8xv9(~7dunVh%`9N%BQ$2gkq**O!C;VH;t;%Kx zHn~{78?D5k5$x8z5yL_Q%dcvIAr8cqQzA8{Fqj2ly-h8vg2D@0ROCJ~;^NV+Kkqc7- z`Bqg#Qq|lo9mF(~<@4-wa?o(}US2@7ChwTPJZ$1zLklkL$p28iIB+{e$e`0XcMzqS z&4%^!SY=J7sfVAHEP-b~_$Iv`*;M4DgWvhRlHlyP5K|}QwMInOuv8r={Ra2)*nhD3 z=uMW|)zj(4$11y?XgeJq!sn?+h3C`25EAY@56KxE7{%(eUB2s5y3 zRUP@-+lripwrVakHa4ET8^LPgomWi?1_n3Nel=cw?`^E^);Waj#rNb3LIoT5TE9!xh6i9n=<++yFdEYZ2H7w2D$V_V+?&d%=O==iH2rdQ;*xJc z9Z%C|j3qQij>a`tG%Mn2jriJu1J-tL;ftUYn&i}1uTaOk{a+>w(Yt-0(^}xex8+Yc zZ22(Qda7+OL+p|Wr)t$SXHVp&dFr+k4{!b`x@NGbMr@H5Db6-d-v~%c*y36aN3sPW zeyWwi;-ST}Ga9-_F7|k|5>!m$gvYfoF6s*|{ba5nPf@K&=6JuZN_U0RUBbUIMyJFq zk<;kqP~=6;$A;Vm9hI?X)8~jtNz`c%h6Y|`4`(*^59b4Mif@9o;}BNB3hM2KQ|9Z$ za5RJ66E8s|*^)wy9}a`N!OcahXHFX6>x_ z#r@b{nzm=ZUDp#m8uAs1&Pm zFf$FZsFa^hxIXLK@oFN_ z3fZnXv~JmSCLsfCr)%3`j~Z0?>2S>uu+8{mVGk|nLC?GZUMH+QY6rH+xdkm*bymU~ zqB<(`^7~hGO!$(_u!ag9<2z-N({4<*$5RS6)e&2lEoeXGl(PGg7OzdUb~YBZ6ahBh zs)c5*xi>J^YQcD|(Nu8U*x$6CZHZT_%DH~i$5>(5m2`ju__2|%K1;1*_CK>vOwO9{ zIV(NYx#(xl991YqBWxOJm8{#aen53mOts;VRx{_DWK9%1r-}T~#%%GbW9CNmi}g2c zlfA3ncd>>v&W|xc#I802!5ul7lLuPl^$teMkx1{HZ}zlNg+#=*b(+F?hchFYz7op4 zAX`8|w`U%ep6b`UX=p4HTU-;cVx!kk&0iin_tZjg)YW>S?fVstvQ;TYsW(yNZR1}^ zPLujScvs4t_aR#qE#bZ&InXDe79X`lyevF>X!`}L+bV%3U(VbIAQNXD zPYCPtA!iJA_=rwcUC4G91{3O%+ALb3R^dtQgU$)?-Jg3i4iWrfvQmC{_pH)vW9@q1 z9YLu0Fnn#B@6ER2gK}}y*lx|?xl1ztuII3?qMF6rloTtQ2NTXPvmRJXi4bH(7#*cp@~r$tyHg)ZA%rpmHC&i0 zrh&j9)=kj&Zo73A!A=XwY3{D?7$3Q{Ro|~Tqc)rJXeB-Bl$KR^27&Zy9a&fj^9o2s z`-nbzyGC?eO}OgYaq8!rJ7uUwuH*m9hpuh^bs@@IpN5f8e?57o4j@ASA<{=y8;7#k z*{|-&bwmyA@FHn%U0FH6lu}BrpV1}u9!ouTYKK$>5z%Hm6n8rlRGSxX*T5WkT=Pps zckNPP+ULZEW>O!1Veb4>dp#VKAn1R0)@~uVa0&d}+Be=V_)|?n{g7Dn+%F$CY=8=d zx{CU&%XS^Uv`N(xkAZiQhTrTM{J1qO0lVfTkK)KIyIuhxQLm8XSLs|9Z+k7AyhCbG z^GT`2)-}kyUcK%I6>}0A_Y#V-janbcU3E?+O`|s4=)W_TF&Sy8(bM#zb?5~?#XL>B zYHT!&lqvnS8&{n2XCr>m=JUkZEgxy9;771|}1loZb>G6jyoA=_za5)JuF zoO|l7dkTl2Ek|E8m=F%0n>;vZzMdI7pnb46B~iBP`fS7E7#K85Dyfh4MgB~DoG@mRWQZZ5$5(R^4I!=uOW+uW1mI#@Xl%Xlm`B#Y3q)u*Q~3jzg{$d{`xg2Gort z?;IcI3%WLDiG>E1b=N6VpG7{K!X>WVr__cSDbz>Bq;SNinrFr0-L~9@{kdIrdvz>P z&99;cdp4$gpUAG;8Ij8e4T^w0yyKETL`4EQeamN-Zke&4{CD#Dn9W)=V8)JbxI(=f zxBUI8<-k{o-?4WA(?8AthNVso$%N(tqH~ck(WF1sZwGNst;4Z=9j3rZ`Vc#c4ku%3 z2sjP*DK#VIk628KG3nzqyC2|algiD%ZO%QM-YJ4S82VR)|Ua#8|_axE1s{ zuMd%@#@^BfArJW=tedEyzj87uz~}4cJCqN5d`^toUrH#$%1_^S{(>FMVdpWS1xDpg zE8ejtfDSYBnI6z7|E!)ZxLup8Y(b?{Vl0fz*eWo8y(Rv3hM1hr2}j%5Xd{5Ou)Bwr z=Z7YiJW)t}AvrH>dqIFs^RArXOHc^eDGSeb>lR*+@D$+Z&O0u!Y2qDF&DqHDZP;cD zmc#}pJx^bH{n4bA=t6h_?0d@oDcF~5z8{u>ogL21m`;ktLgd_aG|pSU4qcy_PeIe_ z7JOr+t-lq0Fy#-EruQtLpWNMOGygsCYf|zHH~sD`sb&^!M()8gOeU&O+h`n3`$?-+ zl`{R2oP%EP!7GJwV&fm5G&SGui!_kZG!`i7xKJ95!S72s9J<(Mm65XFf}4@vR(ES8 zzOXtwS}Etr#QeqDLGtqB6!~DM$Df|UG!G+VbZDM3UU7)yPkP$Ozz4jHB*G_&*RrO5 zzF!xlGq=s!IMWob#3u>$uN|CL@s}Ak#E`$bL%lVudph=-roa{UldQp@##d|6F2I%a zygD6YCsV@>{yD+o9OcG^RI=BR&tYDtVZ$9xiMW)S_%`0vtj)=SWGmvaeR}>aE#6W& zkX;^Hdy~@ZGR3Hs{L~W8Lp5g!&{Ao&8pppVJTJ0+A%)7J%AVL~K|U6vI{sQlvQF@4 z38?L?1q&}Fk2Tx8O)C5{cJ861@|JnlhG$}-@)&dY-R-1(BCugLRzhV$D6>9$I@^(4 zFe?!=2fyZ-4Qd`>Yt@#Caa^#N2BuX|Zr)y$ZC_3xdx=LlQWzRIWe*vSdLdnS<~aX| zZcajn*0Uf{XK}jo+UX85CzkN-t5%Ll?Zf?4v~M6G_>8M!c%NZ`FxC40NF1tnOe0>d zethYGFOkjb*HNct>gqOoWN3$T=l9EV@2bQdUh7{~5@Tl*{mDN_V(rtUy4N`0^ zQTU_Q5&e%g2n;5xB!{p4>!MK`4SuQMa#amW&Nf5z>tvz@|1C$Z+j!Lap!Aea7W96V zgWc;V_Fn?qyeqwD>|zY9aD7e(4t7jFu$QJgwsbX1I}D4`e%k*AZscWzp?wvimQ`Uf zNMX6Y{f{}p$H}G!fk((^#~ZN230TDZaGak*S)qy14FBr(v3Dw{aoiFa#8SE5;)Kv>th# zj?o*?|G(d03O&O`E|=qX+#zm@p?(6;@Nlv%XqQyyE03sc0-qGmM<;DsC$&GQm5z@l zG2j8^jjdFL@@sng@3JQTU^m&W>hd9|n`X3Ah>a>`7A=>H*|sM)R3>NpvIlowJwB3Q zS8KngTZ?|rb2W2sKn^x@xHKSAk)eoRlrEK=5*vZ1j?O~WZe7Gm;J(UlJa&q0pRsU$ zFE%p2yr>9krfxAo9mTG{XdpGEOFzhL;o_Nb>xn$jj{_Or$p0ei-{YD5|M+pd@+zIs zNkXWULMX?YQ>7^9vz#mDY^s?VMx{~;l~c|+A2&HSjE>evY!ra>Z?whXc4oxqcOzZ<+Uegz69mm(2=u8qjLi-R86(i zc*Dk8J{|A8wv+Fn|XuYfbHamo-}AO0=;W z2Njq;n-2eBTS){5m^l!-EV|KKufbjcogijkAM_5}c__Y0#7@LgDbZ4`R4ns;t!ApW zMBAC2l6C4P?xvyZ9_f?bP^I29_clnXzqsvx;Qt*)tez3aG#|HCN@~8q)$~;n!H4yg zPW~u;?nm7tyzXE5$X>BiSun0g{8?Wa70{I+NY6z%gYY!7J3wir25GNbdnATzrqSs( z0@#kSA2QBKuLA{L9X=@s<8}U>OSLL%U-lL|G0095dQs}{eLG@9MO*G@9aF)bvv9c! zWG1xN_&VOAH=H>eJQA{?R99r?Lm?D8a2{Hww0)BW@Gs9$?|4mZ=Hn5MPQn`K1bTMx zeQHfaysoYyIJS(E?NUlQz~shkWEZ%L#A)9>)o|TFW~Aip5y+hfXo?Z96z#OWz5MN@#$w_^MCjJQs3WJ)l+ zx5IZ`29k`&`u~&{ME?@iq8W{7YU=8IHj57_qF>nMrBpw838f(PoYbOL$o|Sk7hnHz z95NW3_;l(cb@sgFqg0QsnvHV*cYDqT9J+>@OfocgMboehgs2wvX2^(F)S>bQ&Z!O?1}cMM*%5S zFDL|Xd#dm!N_e*@)y?XYy7ar;7yiYI^~=JgWwoZ&p2(so6!po5lz#2Ec(X5o9y$j2 zG}H~=7_!j6WKvp=101#W+BlsBD@M^Ga{WZOAy8Jb2(yS6c_4l3u}Wx|bAaCZ$@es4 z=-AXj5&g4(xXN#{SqT&|bbiF0F4dwMgE6LBW1skhocUgf%(og43 znB{_&@lo!t9(1XoiQBTMFAT-hO?RS<9_Qr@_gpGZV+xOPhw6+PrezoMeza8E)cZL! zBYZdC6bexSZroV*Kcn&DLUF;e>rBr>{uQ=tSfSNdKAeLqN zELhe&Y+i0~B@6V0PjN5T4wgdozm=P|8+)}?s4DJ%*IRK+A<&2@&<&4K74u95n2!Nw zdh(l?&c(C6>*y=tC?}cb0r}v|-5nk({VTxNk$Ra7o!ys%RPa{T>XHxE=~i3;c!>qY z0dRiYbgO_?=F-Zz;3_O83X0kYVN4Wm3%6Avj_hJ^R|(! zogWAmt=&gym@ryu85StzHrNxeCqe(rcS{*2Y}*5JM)vpO?G@NYW}!8*dhsUDxLr*sm_f9)?Tb(U@f4Kz0upLPmnsgXH4R)PV&gs|oNq>BV0b zCH-wxnJZjNrDtB@s)@E5b1;J;cKAh2MC03e$&`+j6muo|%KKRZY>nm8N6ewrgD-Bq z$#g4p87@i%H&@j_gYz|9Tc?s1oKgBOeL#%BmoPE#!=9K|(jp+i2&Ec1F!2$AW;-ow z9*~q)Ra9zCsY%O2hMBuUzLGXJ2m*wVYxQen9?VRpndmP+u$?!o&$ zQ$GnOzqn>m6Gxo*4rdntl|qd5y<15=$I=gvc9?K%_vmY#_^BD0w#Su~MUT4GUs@6F zQMXey(YfHVk98D6^#SJd8Ju?{XJQv(i=UTQzjcT{Id6zV=H5{nt+fkq8CNS&i7%wgfYo; zd-|tdoxJog@+FeS4gUN`>%P8-Xv!C#ytgjEecbhqX<5BWz~CETWXb})%!9D7Md^~( zJlD1&sAE-_*nbW`qnrcBeTETq>9URaS(T?SL&^M(2~wb!|w65Ta#AHurBnm-iOe2%NRG9YN5d#F0{ z=xOO@V?f|x0j@DmfxDo%M6R}43mq-^{L5Uz18j@}Cb}EsEfx>WGVAKjp-v98R9mhH z5Ty_M6vRA2je{@!5XHweP2CMXv{89`E5J2&?Ehoy*Tw1RO_&uzd2!PU5aSA+Q8yd% z-X=wxbEX((@a9JO=EU&^6MyJ)?C|}k@DJb+RqPgD6PHjfzqWJ7BvUJNp4T35;087F z{@{~eJcfjo{dQXCBkewMV#vi zM|k&9cbt_kHbiyTJrAYaf(PdZ54cG`0d!j%TfN|Ss%jPX2YPGp2iC{ySLYGgfu4Bp zGCH`yOI-Da1$4N_arA)at@*{aj|lan_IBlo=p@rT7RnbM zx%JwtzFQ+T-L<&&e=?;X80n6aPWJ>O{W#hgZ6+}jf4omeFY z3U4&rZYw7lxOk?WmL(;WTo-9AZ%VN%?mi!*;BLyg6#VeXLCeu|S>(h|>6g(_3Z7=e z+Tw`;E*Xhh{=;Vw;M_lVhYe49dUd1s#-tZd7Jz%5&t6QG3MJMNx#B18s}VPTzt3)= zK}LgGK7RlQxh1FaP0XTdR;Slpe?KF0w`lAAtd)awybAFQGv=O!;rGr)S=uT9 zc6ndO!wkC?7)GmOlmGbA0bQY4F=xy7Gg*oo`+Lw1(jNHEEaqnCp39ZXi+8m*&qei3 z(7XV-aP4SB#WbxWgwRo)QwFjb1xu6aXp%(^dOu4ZJ9|1rysCIAqd}qrUH9-}=g@@7 z#m#7EA>os_^zT-Jq%xZR8AcrN$f8arA9NCnl$LHT+*lth{NcRK*Nn|iyqQ8~0_6c% z{%bv4%L`->_jT1M{alE#J}~pD^SrT9*f0l?i(YZn&j|od5_@QI=s-vEo@HO%r0dp` znxWooCq~+yrvW|3`vLv5x{^(ve75a59wKXS=u{H;tkV22-9uPuTLeCYCj~qcD3(`sI*Smhs(U1<>#MYE@m+9 ztMGioOT<*kX2^g+X!p2;3#0!v)mq9-^2hv<<*T2!H-5}5b1)*n*W0F4rVWITu(Ywz z0f=n<(kHR~{6;i1V(Swt%qeRIIj=C9HP3egAE2Mi;JLkcw4nz!bx$8mNwq`4_i7e` z9~+-+u8iH5C1pf31krVfUR$I-euXCQ#`_r&JLSRYh*aKbMR{fwji>d za*`PxjOZ_&zCS-OSl||jDi7r#fnhCkXvLiHOE+Zev=9>U#agqk)7>Xgc7e(9glM%) zAt%VF|F6|MtDcfj6QSiy8}?fK^1Br`9jaVtq6~GPZv`bSjt8AZ%&xjG<)kZ(6Ta*A zh`E6kGZZ$`mWSsC-=p<{u<%nt<(OLQhY_Yt!##!ypwh+Mj_JVrFTu=AgctlLDCMHi zoMtzszGUp@XOU6~c$BhWwDdFI#XP6aj% z5zx`Xi7K7+elf89M(+6dPuqyq?m{Gu@@hWAWr7Z(QLdA>q8Tk>WkjLM<-QL2p1Wny z>nGPN{;wItO116V-$~V;(-VCxVELTrbk1Agf{UdEaB7zOHk0|h)w3OEZ$XOf^J26P z8uL6^u8>c`q_)erQj_xo#Aitf4D9k7;9Ru+{D34iS@l_?G?2h)aAvmnyOoapa3Qc}SyVr9VT8At}N1snNgnG6Ab65u+n;x8?q_-w2$$XjOfu&QvW1U%p8>B~IE z`%SOdbKvK^A8x=PlU7L+4=)(0B91#W_kOa^0t(UbotayTidxlav!lFtqo0HcKno<+e?=w88OSsT%d+N6v*OSyDpiit%0$~usC!-I9$ ztTXeNp=&wBL2n3PA?8GMj{UvZP=`!_xK76>W{;iP1<=V{?o#Ou5c)ghyIH19n}3QM zfElzeX}T+EfT9exoo{=FYEdutyf4G$hJ}LGBUtV3wb%C%d#fQiR5?T5OqVANlB3E=3 zq96cc{51%l_A+i{VV{vs!n|VU9na3|P~5IV&jKZt?+cj zS?)m>?BX~kK`0(DSrqz`o|lI}M|j6q|*5i6YB*XHBR#}&g0tAF4{}+NkmOML#@x)n-4B#o3E(bQ(U4TkrX?!3au?=~zEwm^o1~sF%X9k?C0a#5cKOnnh=Khprj% zE1b-h{0nPQvyO39lfT&=a(VI7Nx#EgyuEw7a;V;;j*oqGxjZq3JM++b#|>7Wa5O=Y z(WL&y;D<-!MdRTAYn&-Ml|$A15ZzA>Hb(3GqRJ7ry@g)L4~EA;UvJ$IZNm5E89jFE z!k9+JX$Lf=)Ht7Kc~@Dk56ufb<4jvS9FX;K<%st5mmc%}02HV4Y93MdaA5IboJM$~ z8LID#4eXg2$|%TNT}Lw>p&lqy{ITYgfNDDLj%zB=%?&i(-;L6==fYu=uy$aAuThx3 z`PC*yF#Mfh&Dd1K!vhp*lK*7fi+TZi-tGW}xhM7t9JW$Sedn!mdwvUNf^@7B?ry`m zGlA_o)5`t>;EFvWkgE%C3Eob`M$FCacE{463k@Gn$mcZteaE;OHRgxbUnsBwc+oMY zRSt9;_{);x#>rK3-KfZvTO0pF=kF4>nkiqE-P^Z58{!{lAaB{76>$jtk)OKdmums{ z$**<84tQTwm3g!d{K;)tT~_Wf0*9=ysMlW2F-_|gK#7irMrpCe%(;G2B?g7}k}uB+ zy3F#}^K?EwIpt?h()tHiXoPB_hejv(MgVaF+ehd)_NNMACuygl$m&1CY}+?n&iuL_ zFy5A4)Xm+Hn=|eAv1y-s360TiH|xcD&Qa`JL((r=0bz^wQ=bD!We#O5L%Pg`ywLv1 zB+`s+tG8!M#o;=iZuJ6hFS`#1fD(@tBygKeN1?Nl+d|vajU+w~yXgf@P8wZc*Qur4 ztm?5;Il5?B+S7ywUSJ3FwweGF@UBaz&42G5+ooSd`D=cc(ScA1 z=&;V~`PoL!<@H)P$roxYkFvId5?NVpHiX;nJAjBt_SI1EHdrqu-0#EHD<7r^uSU<+QTg(()tQ8~C&}g3 z8S~CwB?0!N6q#d>&U@}9T!sAtRlC+U!4|l$37iE0d;oqVMpe%>Y3(%BwU>DrcDkZs zx^`_f6kI{)929QW4;|GK+aaR|sW+plWIGLgm-$i*baBK*cJ))oJ;>%jmE`^26;iMK zYZPNig7NN5;ek$Wat>w)6nma@fMtNnGxj63a@e})$UpT)YPVyetCaU@D>UzsUpQH; z1h$lUUZQ(l1f!YL=EfU7B}*#qDJu#&BGZ02u7K9z41!5xg1XnI-dO#5=g#sVI_X!i zhQA(ZdZfwLxl;6i;wGObr*I!tX)_`ST)ER{K)83dbUX}?hJN2@aB!Z-B;UcA8WPwZ zBaR2SUY)zsvc3Fj_k=4S{k|>@)hw~;Ol=G`^wB{*8E{pK*`6usD_L7${W(=&ZW_qZ z3!TmwT&CQd0M`CrFayAUs8mz_&9Ule>39D6nEpAWa0slKnrz3ui9b-0AJCe*c~GAl z>lK-?$Vuq%?le;Kj9%i^^g-1c!YB53>IRAGcBhNZBTNm)41UEP=9J< zjF=dmBPQ^j&&CwE{{q&-aCF*2RKg0O)ND(C2iu|374o*pW`ZKJI+~ENy6uH%3s8G6o{RvYQ&~VL$&QJUvv`OjrRA+z0_6n3k9LRCl;pfje z$IYpN7dE)UsVC{cf?t!l*m)*5Wg2xs1(~ZrEJn@WO+OuMaKJlcU<;7F5S`Au^#&5P z((pfF-AkB)cgJ``7qEF8Jb`!1>o zY_jLaC4y|46kfuP90O%}&NJ>PAoH>whvXCPXyXz9oSm%K8^>zD-MxD{kZ|wWzV{Un zu~UQFL2(Kj(f|~-*vryrMv- zP3gr2jZw?jg7SMUN858X$71=!^M*3+O35Wxb0noXz23(2rYXbkx-7o|XrR>B-i&h{ zkw($W?d6>rfj|MGlwM0%4pkv)B*@yTKQd659o=ZkcnMK*tZ~F!%`QBixC30hZ$bMP zw|#8`cU%w0Oqtt7Yvv!@jSg>@d38wV#QH9ep$N^dX=koBNUgc~%~h;!I07qqN(b$I z>t)hj@MoWHNbpd7yjdC@v``wQ4aQoYDW%13a%|j7t6(9cRWL};&-F#wUj`~ofHbxl zP`9VVzLmGIgw#BzZ-2K4!(1<4ORXeV!{Z84-B@#rT5`OJ2C0zESC0f3Kv?&u__|19 z2(s?sVc9DI9|+VIVR$un3hGzxSFq->45d;d9+a|}H?0hKaQvqxdcf= zcJp#{7FEq1-guzEPt_J{?@9Q&U#pwl+?CGNacXt3<%2VS2MvTWo>dvg+9hG5F@gfm+DA0eR~o(zeWjfCdVJ89?4Hywa=-0Fgm#s zQvDBD3Q#K5#()W}kU*Bm9=;LpQbeb>yq~<%)cMOi?a$-H^oj>r z{^12GAcO2**F}Wkwe*eE#h#v)Z9_iJ`GLLtjNV%bK8#yyIfIu6;Q-BNZA7?0P{`Yp0fiNZ8 zp=RS_(pXH7Ztx{9X+l}l2&PO)R)LV|9JfUoq>esW>l8;4pTk8Tsf+}^Pugz8pjmE+w!i`%YP)w+e~Eq{7d8_|Cv2Du8n(^$E)|*_F#)s zTKes4D2;OY=hOit1acS6e=E0-vL18AADeUGm|*l6e5iiA=EeUTlC(Q9IyAo}zqMZ< zx>ylw@Gu?DE$aRKJVY1VCc7l_loUJJ{G3@HVi3sBIJtsoH*?b$qtIVbfXCvrOoDI1h)P)Qh5qt%UB5xvr)A}~|QEaU)c_4JSR_&qPc(J%Ru%U=eIanPXr;GA<&<&)%>$=sG3W}P<+D1Wm<8f?BdNn^Ml zN2NQ)QZ7sPLB+VXC5XCM)gvR&A;H1Qt>sccDUQ}=2zrun8e_D!G5ksa zOTYP#ZOD+l;DHx3lMps?CVo*jVBW8&$P`>by9H@SsK+~MAc~3Ur%R-|Q;eO)0T(h2 zIy9evP@la^8ltRZS&2aIkVH-rM0+t}QR46Mzhqb9>Q7y+^W7PjjXPcjBE2{}Ij zJ-SsvG^kahztU}ia%SU=x8Y*^8Q2S-&hh z=;~>lUMO(>S^AbRjTGI4X}Eb*rg%R*dM#c{`@}W7g~avLp*XdpKDw%;KL##am3>ic z`I-5*X9SMtiz&z5ovDRSy;|nLe!JjHB~MT?|tZ#k5_%m%{e)lJipMs~Llp39<@9t^eN7Irwv4_+LFgI#U20@^fJf=7bp@OuMtvO*OjEM;Oo(aS2v7m&qlCCiV1 zt99k2*|V~K;~2a%;ArhCTpa)Rt8!l#z3?H~rNR;4U-Z7XtF#>La;$0(BLKY~;C^bh zr}&}?|GjkXL{qzqeRRDxEJ-J5><5du2974a@llS}8=#%>6g=%*X=UWXieg=2-T{U`A8HIWY38)^RdmN+G@VxefkYnQw zd9OCgdG@Y*TO*jW^q@~Be_f{8S3egM2dn{hwdaYgUSq>#_mk^C+SOj8+agyV$9YvD z9kpX7n?MW^Lq~XqhIjfypkoMt;G=n;nnQpLll=FJfQ&qlY~C#tHoz5D?DF6;K*4zb zav9{v`8L{wRRlpfhtxl>lyfD*WA+H=i@vV<2`P_%yT-pf*+~q)bT214uxoufI;?Ep zO(!PyfYX1%=zN#AC-UAuKl|V9Y%*&r##mPi>Nk+9{BZs%GaAyqB@e%M+f$UX5N%2~ z4j;eG;k+>q5(aCCrBgvCuGN-Xzxi9xbW8N;0`suddvqLKyX@_le>kNF1_2?lVQkW} z@r+5s`2`q(EX{3sCqA}U&^FJRCST`rK3a1MjhQ4VS~D%XNp|X+X%Xk6_{oD8@w7jk zt;`*UX8qb)UwK|b{!^W?{&=q**1qYET5%7B#`cZCUot+Sjvd$<{5`f8P5dLro9y_F zg7Fh^KZ%&1qSOw&{ag!2=buCU7-uvac0_eXTX+=0PBe|m?GVhO517R2@70C6Wb1$N z?#;767{!|8%J!ql#`3bA_^yx|Cj8A=_v489m^rT6EyY3qDBi@RSJ;4I037*cR6ueM z<3k?xKY{7uqA&Ka7H#v7{+|rK>^zodzRhX`SAe3QY0;{JCIjX0-w5=Db&4o-smiE& zYJidwUv`#&z=z9gSOx3Cf{R*;-R`;nI}qAmXCS-4$Q6dAP#|er*lF?N9@_nB>%TZJ z_YiVSL^9SOv_QY5Wq;ojO!P3Lc=WQak6EJr_x8d(-pd}SB^;bvns98cEqgndn980PAhI{k5Ug({Em2iAR*elCAr|8tB+Yiv-#u-P(w`_-e`Uc3m z&NP9Z5XT>{8dm$5xZBDkdV)eALUC|dG4ok3OI2ro6?xDNU%Mz8!k?-BU`N52AL2iW zK~pfMAH_}>e3vwblX$Nf6VOl7Jlofm_P4>Qrnh*#8vGSB%bm=bnT5!>r5_jY0xt6J zp$~so`QdaeP{vCbuzF9Xu3Y(p&iAlQ(PA~`y7pL6Xp5^cn#`%l8Fng;iYIvD$@Hy& z=T&91V{Fi;Z^9Ergim<9Mr&v(b?b@W(Y-}kd$dWLuxU`RFX>;Ys>!nA$7u=VG=RFu zTYtPXnl6@o!$VQQDbX9&ueQA94JCz+VWTxb5=&go5xqm9=gtj?k}aL?8#8 z^Mj+v$`1ZtDj_<^FX9huhn!vB7oG9%>O(+s7TEi9!|pO|lIHOgj?tl3ls2C&^o+=h zX41IJVH*)IvX>o1l@~yAH$OoKM2+*O3uJcILN5f%o$fU;>?PfF zdehR0J)`s~1{;*J8Ia36xrm&PGy0I9Pccob8gy%ZkmPQ($qit3G)=1yzYKkA0KiZujN2T`0L3@;2il&Z%Z#wZowisr|wIS51e=BG=%J>CB?Xj7(ki z;hv}XU9U2-wEW-eUnu-hjEiJqOqx1uU^2UrE&olkqB*87w>?jTKHbm?3z%yWbPRGl zS2*$J_vHplnLDvYGOL}yT?)ihy%v|bs1@DIkodxh7l@D2td$}Q3W@EUuT9hb&g4Aq z!M7_$VG1!H|MO?0Uv3jeu!!P>1Lu%!n-E`13YqAy) zdMHb4!tXpUU>XHh7!cAHM{lvAr^_Kjh(E9$@g2-e^0HP~j+Kht)o6uwKCBkapft!Ra{(u} z?+VbMU=kpH{DzAInvck=AoGmTyS`Fwf$W$hKe>Xf-J?CBHk_YSnP zH|ghjOR6>l;pLV1O2!3TwF$!d|1=5)Mhw6aXu0lcZY}gh)qq|sQmBI-^hBH`WGD>O zU7i-7N#YYGrLcL1R;dw8(VWnRmZKG5YP9~N$t>RA-UCzOTjOG@kmr~In~miU#T%*1 z7E^#(0#Z%{%2yUp%&>ZmNDf=bz6&w2DLCrc+sBT`5Dtc+WG7;+$fL{cb7DycAr%Ih zmHHnTZ5n;3>C4Q9o~|&3sO_SnlKl_~&aDnbatC?>NW+?O9B|tZQwrGlIC9 z_acFW$cyiVrqdnZW#eH{wc$jX&2;m~-dH)c&L!JsSl;2kWgZe&shAee{DO8Os6wLm4 z4p;1vv3w_~tw7ah-K4f4)-i77?|F~PhS(otZWK(%)nqfxS%Q3*vgsD2?hIg)Z z9O748GdwOiYwA<=DRPxU2rk2q)yv!|f8YKz5Xz??v823;J?L_{Pyx$hE6ne=H;i3r zrCk;=E~?&ZTep@_Etre_j}UMVd)zH9Da!F_A+?gV-u3HTeym)T1!u_Te5*pY(@bA> z!&j^H<+z%!y0+5Agd2*s^nd$bREI@S<6I6O@c@n9DvqP7-Th|@5AN9lqOdSS1UGnI z3rw(m4)WipX0!ShF-=Xtc}UaZMgBXl`**f-`D*o(&B~mw(R7tbrKpgk$q}BtVzx_-8@G@=D;wr>~_9Z^l~j} zY8FeXu2uaIKJ!Y*KNcAlWFFHmQ~1G=+GiATaT2@XCMpS5Ve7bEj(pxSt(a}?jeii= z930kedpU3;P1@fH6f^PcV^<)qlXj-=hrf+trxYly5+tW9_Qdyu&j^5G0HQr!R<8Eoiu9s|IZ1MN2?^JlD3+hhO2(^ z2i~Z>ks@kL-wh(KMbyKxdRVGtPq0Fm^?HO-b>?WAucr}-I)F!2{u1(%&OxZl^WW&C zv>@-5h!`P>D81CO^idVobdfGCD$tp9IU|9CJ1ApS#A8xb{?XSDDC;^ zO8JB$?v{QxT!OrW7K(=PI~j%&d9{}KT7E_EE+!7@-flqPcKl`h7wn6T_}R%HHfZZ3 zqFn<0r`sgp;%okXqaf;5ef-spOgrL~ZpR$v>V{~TELr&Nc-yyVNM?jiP0~3tXJs?qyi20G%+QG=%=3y z9>*$#JkIo-fTznoMT2o>h}c5yas!ur6+`_hU!9(haTMEQ8%G2D`I|MYhDFiEYY6mE zgvwKGb7u$J6j|d5{#W#{#ksJR+apkAoYX~+#UAaX-@_HgdC_j*d!PR*nzjvPs^~W+ ztNe!9eg!V8%cj>lN!0~O(t3YPfvqfE1Vr8{PBENKtoWK%<7U0PM&5+F+lVMJsPei1 zSy-*QqX`0%YKz1A&MmAqnNq9Q$HM1TzBlrxkzt=z)$>`)7R6)QyI0rJ=|QHefv!n2 zc*KI!aBxYMc?2mzST}|yg^#h1%dJ0TH?_3BM+)eRMgL|N>Q3m{yA{9nj-rUUBv+gr zi*P8EJ+W0qr1LADne2W$pj9KRWg3pXRZLIohfa1j%?fz!d{yk&Yi;Z>aolC0| zFD9jpgm-~{UoSrF)D@h~{>ojv_7vXUZTGjLd+*6Bg`6BeU)%d7H|!jCRZkHjMvqOZ zM66%6u{3~+KPDBJHU_U_dXyxuWPkVWg>7^whGz}9qNp+-7rmw`!kKm2B2UE6B34V6 zmybzgldg&02*7+ZF@7j||GzLM>lXD26AyomycG07l}}gs=VtCn!89kxmeNBdkeL-L z4&UUi+%D``i&;yfEjO$VD%!L%ujMUXzg1^i5~TF1wFf#BjcVf{OS`L#gh1Rrz-k5i zjJV{}^{xIq-w;MLvBxr5N{4T(DMKO@3cI zgf1a`Q_QaX6^Hb+juek1n`m^bwMN&{SJ~1-PbEf0uPyM_BT<~f=>yz>VK`*PzfyTo z71t!EqxD=M7=u?1uu+xcPAhci#Ofa^2|jalI*!c=xd(cTsxEUPz~70#+qpl0=ERESM%<)-3jHMQ;CPK5-BQRY@v|KS(Ps_{*B5pIAkSp} zfp*RZ@q;6ce9ty4CBNGGZ=v!m+pvVW{dMQX|3NxO@Yij)F=}_e)XcMhU3!7(gDhX< z;fxR5@d@C2C*_2rwMw_(aJdRR^iFEZesTcZ8Cplqlvn7 zhqWO~&wp*fW3bwh_Zh{D+V?<*VgqegW7K6-aA3;clRS!hZ11Xjgk~GY$rs|ReTU%El*S?abXN3c8d_p^aJ1)kr znZyjmvxj#7{|$R}P*3z^^0Da1z6f4RrqnnU6uVf)9~&B43#;vGF*6Nox#ON1LZm@c zUPm0syy-WgTCm*U9q#=v`9Fd-W3rH+(GtJeEeM{vI_Oj{nlaS)Po^LJeLH75)QDUe zC7qYRE1yoD#UF)FoD8GF%tVR@Zc(-vA_Pp{7!R9wH?9(O!vnmAiYE{W1(ind>y~swQXZ>_Xj?@+8+6Dl5fVrOrP^hh&Np9=@ke@ zU-fn@{O^6<>lUbjM}GCv*wqOp5H-{B7eyr-+hO?~!hZ&BqtUT$)~&!yxJQbtP;`&KXZP<*x)LuV{_e-mklQqB5_-6 z+F^)$TsLupJM^*pOKgEWY>m{7!>eS4JN%emn^F|gI9(8uB0sWsu8WjXDH~k*C-rOUeex(RX3+kDfOVd&e#{rAS1=82>GMQV|_1(-e z_vHx1wOz)|!B`y?>lc^-kt%yP%0HdB_R%C~`c{NDu`$pc#Zs=mw>TR8M0Tt6dq<(d zGG*oO1--|%bjb^ai$&cIS%>agzqoA~@+>EUGc76)Q~cUQa-0vRZIr2gbpnYt=Xoh4 zEgmq)*gvF-ZdIOpbW;=*kX97u$@f6Sl2Fa}z74;lFBU($p_tB&qSt9hxl?CIP0 zNGIFiJg2nav_kXal2hLtwsg}?F9-Y9cN(~4eL;3FXLDYN?;6@`Cfqobp73ux4G40A?(<*MVpVot-rQ@IPo^+*}ls{t#b%QO3u z4oaxku6w!HJx+>N&pzYEw32)Wx_zChOxWuvY{nz2uQ}AA1IO7N!;iTG3ma}WE(;@U z*I|DfB7^4d6+yoBG!%~Gr%aeBB*D+|w`BN?mIrJlS-nTCssr?FrPgzS8zOSPmmUu8 z)#K_s#29ITHg7N~`B-k-GyN4oMJ+&m-Ie?mm87k5m+63Iu1m}5aD6zj`;4fFCq2|k zydQ>n6Wa{hads0se#|YwzkvSFnsmq`pWaY}e-@C(hQEr#+(WmM;(qfW`aCKo0obnN zt#|b#pG5Yoa}76sHf*ex*o2TfM3%!3`itjQgo|DXaO4ja_vgr+It5Z1 z@QN;m-$tlUCTH_+ir!z~er;ThvY|auLrMf!L6csJ$JQ=rEo!iIa$XAZ_s3+?c1R01 zB;~>nZ}QB^hTP<#oo2NNq4SlP&Aai;KVRViCgj6WMjT6}5Zc-(+JJzeu}P)fgq{CQX0%Gk&6Gj&^f)uJ>D1OZ ztmrmjw7krKGlsb=8a+~Om`+tmbNXte1x`_ojUz;-W4Hf!wcy$oZzs3KZWV^ewipdC z9K0Be_{9-ON)qX>`9cP0A#zo4v27d6?S)E?D~{c4L&D{*q1W7aWR*eKA}W8nV@ue9 z8DZdpboKcepYV13zj^6n@IUX}I`uEIH<(PBx`~eUxr6XiPx8HIT4&a_U!Il?ZElg< z7F`a`>08<8RFKDvcKlj#Wla9NV?<<&i>lp}f-;+sI;U}PKJOO5uydhBHxRG>1idEO*YdcR|!0r1#l~^bh zsUia@x%=q6egrU)E7+@JVXJoVxs3|3N6YOK^khmK>me`mz|wZ$eokG^kt7it`24Mo#c>im%B5kP_d34b<&tIRdqJMPLE`z!Hhe| zxiX#HTvi}lZ_t*-UjC*OW)p!g3`S<2>}#Hg)|v_rYSlpD>rF7a__mNrc+S`NoAhE< zXmX1e3oCawk6455Uaie6^CUpG0zBJ^@~O>;6gsMSpaIp}1VvM&@0uF#tpR2G<0Hyi z8J8m`F6XrCOgPIAz>5F#L(m(?-MvG5ziLh{%NFgQqU&@;Lyqd6$Anhp#dwyp^BJMC z*yH?6`A7O|7Fb5px9DZ9jS}L?u&Z*~Gv30CVYeQZRGHa$9LEE~j3S1&a@xr(uM{C7 z`h_8fW<-=R1*T9V=B(fGFUIA5QllOjxvmS4oH@SULs_dzH1UN4VmbCgzuLp%!6T_R zaJ|x&gi6Qc*RSCY&#RvA1;&^S4C0$qKYaDRP9CeQUwVK!|;0}`;vo+Mp99>w`WdzhoY-)7xI z8`j|Eo%H3P2b$A1ohhJtXEv_CH4RPW2B?4yJIapNGLh9H|Aj9@(kHXKTpgzt3=z#7 zyC*%;N%z)kJH0JK7H(TuiJ;Cni>fYcf611-CK|ASoYlx|RI3V$9zj7@pu@uI5r?f( zRwzBU4RqxORoCV3VrQ)u2k~9*(;=zMd-@T}5h=CybzHuOkVT00+?`%_kU(`P*ln1R z*vOTq#|*;l(VUNhBP+nC;yHEY)V=C{_&8iu^j&3(Z!?3$IVH>n-a z%L&_mt5I}A)L$j|@UmLJo6-_1=BBmi>L*{gyYsI0%b9tT%FC!$chOVX&$6c3JP!|r z3RG<_ZQ2jG&u!T))-jd>vw0@EX!cy3NyaS$e=31-9^%VcDU9N2IuNpNiLhSR3p1nY zM~4=^m_*BWuvH@|Da)IppGcECj)YPnKr-wVU6DuB@5x{OSoO`K7vnwSS*+~I-W@ga zk5G2u;Pe`v(ueX8m(a+5|5D@7rNG!aqovwB|3!PM1w9WWhy<7k?igzF11*?;Tqu2T zaOG!6eQB@f=-d>v5IfnikTN7M7_SS z6nV`@+4KEjE6!e8u&yn6VWe8u=8Hf5oZgU{2cC>4$7lxGR>c;D&VQtAP6oaby@EYg zePstF3?b(D3K?6tyjT+Hy14|a&q15ezgKyyJhF^WIct}rYb|UVDz@~Kgrk~_HrG31gn zP!yb&pB{Dd5vN?4HSnf+o|__8Ps%KA-rs46(H$B<-4aC(I9W}!`4)_BO%v++@U+>I zDU_?h_?j2h7V;jMQ%wUo>Ae;n@1)?E`e=npeS6yn)@MwHP>q`ds)+}es`!2c46tYD z^>HcWweC%%TRY%sx}?0GY0#W`pc(amp{k z2fr>_Nfo8^29j!FEYf>CW~?J4U%Y$sxryyYti3qLryj@EUrk){D>1@L$$qt*<7zziy%!{Zx^C zc2`V!DY(BGOIcn}60|Uj&1>et;5U&AR74$VlKpw&g7133GFfF+^BPi%sm5KKW`7Um z!&1u)Pb0oxhmU?7sNR~{Na8B)nX%gx4RIFkuX?jp=ztDP-xx$6&( z(Uo!EQ|)ZBzis1@iVHma`vz`r;^8g$K%VFZ(rtRQmt_%VP#ao0&FI;sdhE3!=@Zu6 zG;s!fmzoE(<*jhnhFNSN-py+6aYd2)&zfn*yNU3#o1`+$Wd#}*gA(!$@53Y4(Hiri zG^_!g^n#LoF4kA2y*Bm*!o{%)xd^(bt3?QGpiUHn$10m*1>YLT% z8BxVGyYKXz*DltB1S`3I&eIKf_{V6RM3-kop6Y-nj?U6VycNN(>LG8hL=P>kR{pNTCpxlv@?AVe>YYfx8s$urW;%5#3( zDZRX|YqXn|UzgJ4as(R=#s5j|1z1T+9hq~fNOXJ+Wm2!< zx$!{NA$aTC7iHQL-O)Xw1j1Wf}HuL-(tO5>kg_-|QUg;K0+3KvBL5!*S&F0-#dc~Y)C%DAOJkznvw{m8aGB=b`^8y)&&$plM2Ct=ox}FLI5mJ=!;}_BTHLZ2Ejr1j0AI z29zntJHDMQG5Oh|$fak$^fr$lorC$0KR6{#WnhJp)*H)Ci8G;)0Iya7(Wax%h0ze9Sz%=4~Ge|PBX*R8U+A4+A_ zpKWlhG69@KkDIba@bYTRK|}P50EzQiz3ne@pNJ}){7^*tHTIH}xfv z{557-hOt;Ua+8!UCdP15bwaa3mN6;sOenjOYrvXn12=Ha(<|G`3n;mxDLl7an?a2g z@)k>f=iVqeK1Urlc;%U9?emQk$V*n;K@<|*b2QClVxM27->(rvJ5GNrm}GA07hp+v zL%2q&P%7#@x+qszSw7r#@=4bTHgOGRi8rFX=wU((mv+N-(UUe&2lAlDC)+RoHFhex zu!71XnE9ZOk~xAFx3m#X!JL)iLJzz9L7A;2>JJ5EEj=Whi=Hm*Z)f8sl@fI1hNJd> z#-bX{wV#H0C##WcIzf+!Y|gc<6qSAL=+jzMT9{8Urq#<7Uveu$jne~kUci$Ok#HL7 zb0MbNfI4?@DITQXE?>Gddj^-AC9Ny{*C6TDlW3O%H0y@j^Kt(8nh}u7>E=5Z4i;73 zs?bw*XwHVm--TCxnDq&KTo!J>)0KYFb@ZNY-WNerE2m>dSNaLRhxi|+Qbv|xQwPk| z9}?YlGe#YHE_wo5L{szf@GhQgSXyIwlfj(CTe+{h{oS_&2*!uLXFP@e3quCs?dH9<+Gy<`7oUq&8f2pJ7sboULtY zIuknzZk98w*?iivR31=)eYRr6)g<1o=$j*6aLK3<^-F4QWx1*H3jyCQU_?KD81f0c z_z0H1B~%m@w)$$G;v0D$H{=&#*(_I7xcJ~bex^wLbE{!$rH2>Pz$;8TtFXN;sfuF7 zv0y+a*CwH9GrXQpaxa(Uf`-F>>f_|gSK~oZH(JVatL0O#-3mc8o(1Z=BjX|%&12Yi z$S9ef_o=kapf5~OqT^W>Oh(JQm85lPcqN4g#x~+}qtm|LuM}5r{Q6|+jz*%b^N;8J zW)^nihFbmF;?LE8JI)qdrF8e&dQKfsO)$H8<%{HQ!N(ML^&kmsVQZ^V?nNym_@P3P z1kpcyuGZ$n`iG*A{i^NOjE!zt7N`T_-YX87xakEGOp3Sm411*^UenRIAE@M~sU?Qn zCc0)dXl<%{h@&*0c%ywQ28n%Z9P;fBiLk{()T)*8e;etnpQ~;!zQ_L882H;L|AiYO zLjUi7w`iy*3=DBESNPOAcGK^NmimGVjfs~L5pbp?LASn`XY zg_T*H)0hadZRqGfRoMQS{^F^oT+ni~dr|(ZO!LScA9*<>K~~lZaaqd?hzU(~f4%Yt zrgqo zoNR%-LP4iN7yO;2EA-m^*UwoY1`3b^5dG6KV+|qC|U#+B%U8`@jI$loR|LVm1 z?t?>oJGP|u{C@tj3IB0(D>Khmjeks7Xx}VOh&Nl8n{gh;#M6ZQ;j}|Ni@%*#EdW`~QlYLJEtI-V|BXs*U;l0KgkPosNvQ z4v4e?Al5(Er_B~6S>&5p?Wk8qRYe*rgEi3_9_{N8ggfEcQ>XX12T&je8zO!XBV zN%yQB2*#>5@QA%Sj7sl+y@=VH3*yM1Ax_8-_R3cXdR4ml4iKgdU;D)W60iUr2I>?sYniOvoiXfR%9mL=PJUqWGruGLK&M>=81CBXC}9ROp}+KB4aC{8mq>mJfxaazWv9B_)HK;Zw(bM1OtgF+yLWquFq^Az|2SY0^D1 zRK+YyYC7xEKV`leC!Ls(dvjXLa zkF7f&-$|#aq#|yV4bUqdxRqK&!1tK9GrzvGqtamQ+Bkx9FJAaMX(sK7fM}A{ObwV% zZ+5)cYw&xlrlY&6M@iLeM%Du5{Il7(W(`s|{|tG50A~|HXKoUD+331~kn!2GGd7 zq$gL;RTXm|pCMC&n#Jg=sYMnQY7Yjl=?@T|@oKq+avkzo&ce&!tsmbUD?;;-@_}BI z0pm9tRnnvUggOP*Q$5S@OB{384kMf0O3HCoyn8rEnUUJfWz4Q}_vqNn}KMo0>6 zkG*QZw~Hhc`*O?XUkSbcmYDTXlk!3)(-!jcZJcNS^w;rqj_C}d-!DQa&2FG&aj9^Z zFgPF|@V7A$9*yg7XRMDU(8}Uzv?hDFUZV@4yGjG#kb#toUHeih{3_ZZU!U@mJjjSm zfuwk+?Foeyo>6F1(WtJuW{w^$j-36X=w%slCtD7$gdW+4rgw0W$9Fz^5tA>259dS* zyW6r_M)Aai$7Bgv1)8oTD1_h*g8mNs=^$z&|`|M|Yy9iPnXVIasTP zCyu9aG=AVfYByhcF-*M7$VKrGs!llrbN70j<@f(n zp}p{{T`~y9ZH&`c>c;@vnbZXHql;(r_01^W*5$#GMHGzcSWT2$Yo%I0tu~`rnq{Ap z8S?XOtVb^)@4r;O1<_-oL@ebSot`~pTFeRyd{jFQz4I_;+dPBW+N#!AHq62_s z{m}}Z%Ra{|=3OMSUr1w@#sy>zC|xt$w#8rYrj>J@sJajXoK%DK*Y-)IYn>4FK~3hq z%OxBSeDI2+{!5W}Kum60!Ix*1zuHX^zdIc3)-Kkvh8Ay?4rn}^ZHI?KI2kqQrv9gZ zwR9a@n`^NkmCwBW0{F;q7iHNNHZ@;9)6%Lg55&{;9PU6m-Web~^>td71zVGsN!yyJ z04TJzOY{fc&Sh~UxEm0?n%6(R6BzWANQajidclsm#NTpv-MmN)nT>D@AOR%sQ^3#{ z1F;b-t<{M3kI1$&yv_ZG`ulba`3naUm`VXiZ}D&|#wp5QRm+v9)XbC?5i1AYbu%%l zx69}nCtSGAJ*yz-SV9-!Zq#d|l7Woe2iAyyoOWwO+fPDUKEM$BCR((n|uTUB-?j4*)>s9mjeL~xI$Mv%PBSe?@6 z%h0eHQC@;AOVes}3+v}&Qp45C{5dm+;&{X8(VEKRYfk1v_hJKs>n8@hdw+wX#|X(k zxtE*YI}^YxEoZ&pn?+6M`0>30a;9MF3NlXpCW1R+-!K>l-bPdTM7TxjCWs*Pkqg z3^n&a23w;MA*RZ;gU|HfT#8Bj1*(IIbl5XpIF*qdt|j2>&HFFN*?GPa?NQC-eLg|8$L->pov#*s6@C)uXD%FI*3a|Er}7T8m(t&gWWB-{HHJ4Y> zVj6wH7&3JZHh%yH>>8kFb-kBYSL|DivI_TB{EJ%qXQVyW?eZWB-OvOkbs(s{#Hv}! zz7#zd+OQR3#06XJa@ig%^t8IGkgo2k#MH!NhyUkNplW?_!__9+3CdBB zDexFDgoCEQqb9#!czia$u&}hc+Comc#VfHP9&qXaMr)ho{^v@jHN3j1Z(wi$g(*8< z&JDmA?!O4ae{Q>qwK4-D{>KZEix|R39pAXd*Y^5h?#gN_ddaxR;f+!+-5+=2Kb&89$yY1K5Q@9oZT?K8Ae#;t@~kR{DO(q>_uwR!km7ra8L-x-&88!P%Gezsj2C6Me6m01pa{e zd!H37%Nr)zQ_S`K{3=2Vxs$bl3&IxJl^$ZNsp;0e92g}Y4mR! zen6`9uz$AVsrC?M`P}nk`8yvCgjQMqa!QgOVWo;o!NEHI%h?AgZ4$>Kvs`2SUOB|F zjGEX^@GO{^tArFtC`f8ntU!#?%HNHgCsnMyM03EDQLZFH0wq|g=|OcfxoqweGQ_OK zQQ6GcIODLmW?OOHI*|UWB+L|-lWyocUK4)ucV&lxSW8gXe&*2AQ33uW=k#kjgUJef zh%nO5DE-z$D2P6=0Z&OmreeGTLDIk*JnXFAijrTy_s;sPR!)n6;h@dTdSH6G2V>L! z_2JpYJOdF-Z-AHP!{#V`cW`3=)oW|HffFQ{(k%osJ^5a3`WiujRffFPJH<{i$uYDt zvw}y51wKJ@VuRZ&Qr~PO&u}weL#CD>bKa4)=Wqdq;1i2OLIstT4FU>Qt|i#5n%dQo zsFhw5BOoF9mM?pVrb)Z$4^Zm%zln#-L7zZcyo(3*XRrIchLA#g0XzutO@dSn>LUn? z%P{$~rzOT#wz~uKxw?14h;pw)>pbxVMuj|KXyFy5HF#7z3wy!DmwX=eaj%MFuMn*0 zyR>jmV1`0Eip)RgE9Ftj3u~`}r!|q-WEYFE;t$DF{Z%}XD-DBh&2y~rQqAb|VGETC zDqbV|gjXiyVVhlD#nSle9o|TDC_FB8rhlq0Qr9ZI;~06ruWj+n``Q7|YGi7$$&)%a zOPv;9cX@Ko!6gjF2G`zRmYeIil7t1hrgJuE-w_}sIk~E_u|J|>V=G3RqplYJUfF-! z3(RyHb2r-Cko>|d;x|)ZT|cp3b=qccu)8##8tjnTexi!VKh_*|^J;uzJlgk-`^U>r z%4S>_BB0(=PDvq9g23$)HS4PL)Zk>@H~z8o7#k-w3Z&{*T_8E)NmP+bj`!U9hwm~0 z7=ra#EMbN55^;sB>A!cqbx@h7}OC%O-j`uYPFwz(M-N!YAHD-hVa!qdk|30vWQ&2KxjjZ z>kNC~2V5rL9_=IFROb1R#zK1W{((tm>YJx;7dN7tn}{u%OnGHZ9RH$8WXYpgCBXvF zfckXEl$@D7vM!Wbp=euU!I~=>%+0SYOw$+flun=;Me}x!0n6Na} z16f;iQgLW_WL@Fx-{R^8?wIZ?C=BOp^;fx@Gs^6|aqa0^c>eiiu$iyurBdo#&5R;? zo{NSGMh$jwrcyHB{eC0L3RCM7=XttCy3J5IIijBFIZwhyhSeqBCD5C0{&?}ZBX9-Ul zrkRDSjgG^lGan*-sSN$7O(t@+4)bf6cmwYAVv*CaPqT#Hg%GcC*BVqxu>m#kL8 zw`k$B3FdZ)wzjsn5aMwmwUVw`p}yicMstj46_2PGqM)RXr~)BQD$2>I&D{(k_hUi7 zbfDAD7;e9#C=WDHNxz_1;@=nqwS+40kMmb$(}8WON@{k_!ilr~5^Y@mo4xNG&KH)K zcTa@b*mfF;)|GcW-O?F90F2Uel;Ai1S-sD2OijFDrKxYARY}fSLSdOZq)RG=TsiMZ zv7Xe;w|}!}vv;xW^qt0)jVZ>{(l|EC>aY=S*@- z_K!Z!p?ih9bC!%a4+4k0(aSRf_!oyxI2lvWD_)#_I~d&**VdK`Eu@o>gQn)T zQ?Hp)>9Y^Abfe49IqH&njI8$(?;X9g{9+<4_lUIt|wra43jD3o)F zhjj9g zHP1XNgYJDyUc`KNwZ7z3A=h#&vwOCsZjUw6H8y(k94QzKu1{Kn%;LiX3C^^ZNutuz#(a^ z6F4PKZRm1=eU+enNLOZ7mW^7m%wUp&^}_)>ShTR{)q}Q71`Ap-^~1V%;FQ0xVm4by5HnIt<`$o*=~KMDy@xS-j(<1HV>(6 z3TyVYTyZI9lWI>4tYbpZED8)VP;9#lPx{TXxq1On4Ud7)5bVnQbyaXM12E(9ll4t2 z)(?#1ZYbmSwE8MsD8)&sAiS3M&+~b;2Yd+-XK4C`-V8|u5QOEAg*&SGn?*5>kl}a* zPrsUQ@aI^Tg;0B4U_}Wi+$~>@rggHcd>up+)kQZGu@bFkvEjo&EOL)JZ}sar&IX=S z3Fyy&&`0~*ZyDLnK$an|)#2}vL(A&CHCu&lWMwi8y7rO&X{wvOG&P$2h?PV$$-adM z2L@u5{lKF8!5lQ;|;$x(uJN;*UOR~0gl9C;!1rD7!sj5z9plI+4Uaj@g zerTURWhq)%m9V-nYExg*wGhq<45z`Ly=LaXgIfV5_K-6#eiVyu`Ay^h(lIZPN|WZa z@<6`=5kfK(&voL&xZ?0K`IcvaQvpQAYvEgjeVFs@>vb3Vg8hWNN?@!DP{P3|twiU9 zggz%vdK^u7S{%C78!^3ziHW1hEPO=xn|?-bwKq2tmW&G&24S0L4_gKRdX>=Y*c{_M zU=JNnI4yphpXARUq6NhjO^@ql70DLg$t^SCWI0LIiV40+2#YGd2d)f&_407)%&W`;GmPy z*9X+v{qH@_P6Y@FBsB~T zuJ@VSkL#A@eEHJcQZS@QvnhLe+qs zJCYI{C2Xq|G=I+veb-3qH!T`*Y}?*cCD44EOOPYqIvq$Q*`V+Tl@Oa6ylwV~^7OrT zp`DH~DTvUX#h1xbgZ|`JH4WP`%^|d=oPyE=B4e!jkm@^IlgehXn>;f3p7*{sWhKnq z85^1vKFSgf0QxT565nT1Qt2PN?Jk~&_2rwcG%AI4p|=j2e|A5)$y%S`xN=zG*fM^( z!~v^ppv_B9+FD!7cXO?vee+{aocksQn>~jfd#67HWTy(5zfuG^vW`P<{8$LiyTroo zvx+=$>?~m$YN9t5agezowUw!yo}o#o=$=osLgTr5+%Ma@gAPQa?d^mE`}CxjpPL{p z@WABpIRj^d5|wu4os02ptnSFD3v`bcb`!Ihi${hrr92;vU%*J&0YO|lZs=UOyEe%Z zu&@g-z7F`M-i_AE7+HjQX2^9drz)C3i+z9u4E}S$(VfKw`v=zQP2to~7I`j}7Ocs~ z#|Qk{bJhonyr`^)9ma~uM{B*Pbt_~>7!r81Jh=1ImHsa{InQaov>cm8q}bJ-?QGG5 z;EqieRs2r1swsf8gpErwF#rIWEAzj188CW7@eBS5>%Ure83v%Not4PNA;YwCf<)fR zFv+*5`TZzlAX~z-HPF^P7&FwgZ2iCsq}i`2R@rbgtGlSOx-AA4D~`I*v|f9$5!gPN zYYr@-5@~sTCRsC!j}v9_{`OzKNEXYR$%qOIJMw~;-iB^5(~)7D>sDrF*=U(x%Z}^Wq>nf ziZoyg=c;@FEp}@Yl>gubAe?r1_?8-Mp zbG)y>oP-Zsxxk)K#oh-%M2WB(r4|;5e50G>-n#||quq6>+e(5jsa+9bPn%<>v;78M zE)Li;>`WRs?bHo}qD32AN3t?5dY}1Jaq|=vo4$=n)B|P#e8U;l+R8dCyb{);)|PTL zYq|&3^GaA=;T+o8 z))v@L=YAE9FAY;NtSV$$z}ykYa9?8PMPSoRfj@8UCT24{;wcG`&!2sNun z)h$r6DE&HDJsQAnQmwmh&n6DoxP?u9Nh0Euu(PjyIq{6(!fR0iL^{%3i8-zmKB$nz zd~NUD$UmofH`~1ucJE}gVfqCZgtczgpucbe!wOH`T0yD+R!ub@e7pN!ytEn+w|Kd0 z!3WnNSDw1z0N`$3RNa@0cY)1_ii*-@vy~xoty+}auEu0DTV%T-uA+N1BP&e~CnryZ z?S23qaDRXn%W@uUop$OuS~1%>XQ5G})ph=kuk9;_y%AYOsi4#6!oeZOb#Xn~Wu5v^2qH$yWE3tEhB-t_CKQ=Rbt z-Na76cpCjBHoiD2+f+4gv}V29X}TKp$oBg{x}QXs5nL0C;;aj~pwg$=2Wt!Sp5nhy zj%){%&s@!#>X<>N9rR2C4uNRpFV5w?6>Dc{c^@4>FSqtYHAd^CgP?cPoT}%{lV-ei zIQCS>Ut?;AD-aM$zwX%Tn;I^3TGM-^d8NR9v(>Y1%~|+@bESecqV23f@??iou`x}R z?B`gNH~jlu8yn!={o~M?PrM1)E{x9M`+;?f`iiKAu+{J4HQgKB_Nz~A0|uQ`U@P+G z*0`P=JW?KU1o&kp%I+WC{=aZqdwEjF3Db+LJ)2Hh?rilN1YlAbJ z#`VFp8H#-$eze)p|ER{RwCaXeqXH!FILQJF^3mzqI}v;KpFaI0LWl8E@yU@|07f!u z6>2MR<)z%S<)w!cj{>WPj%9fld@Z`vpm3hCz|?eLsQG4xUZ4eqF+(M`!*{$l-v)*4 zKD}V`_klluN6!8g*K;$nfhoNI747d><$v8aRzHG|71j6VC>+j{!e0lK$X^Eu{`XTa zyPxa;&XqI^l=pQzq2-p{z-)cm;QO!Q|BfA!7JW$*Rw+c%;IQJmhfaWQjzFERjG+FG zPybh$@jMMogA^63_GD%K#?H7=Nw~SdA8_vfE>CCbG6=v2kAV{s%kO#^5)>9N<){9) zwsDQDEsQohgkjALKfB!ce`><%g^hjM|7&et(NvEd#XtY=dS?0o;<)G2VOu(j3DNT$ zqYlrE-?-7p#A~v#*OPH`*kO04;fm{Orxt&HaNkmQIy@n)puZ4-TeBQn8-^Vui>n%> zvRhGG+6~`hk=-$B-47mowzjg$AD@_Lu-TSzL*KAy@Q6;blT(^MV{Cp|n7>Y9RV(bv z+CIKt(pq)Y9)a$SOYrT8$@xNh(vXGlWt=9}YIVJf$+lXbsz`jFB<>pyeSAb@?o(#< zqv5Atva@y7wfYvB_+}d=g(OA0a+AD|0cRw?cU&W$!dwpAvZ?!WHixvZU(JPiaL;_j zN3}WRNXsQ?zoLE3@ph?2YTKLH53r|ijVAKs&Chn*)P)Ro=yZS9J){yO?gS1!kMk*C z90CukA21e6)ktz-vltcI%TECRIy5!)RR=9RU7lWkrR>&C01nl(8(G?Jr?>`20~(2N z?fT~2_Q6w4ZSWjx=;dt&0H{b!(ZaL6c_!xuev7|>{||W5g`gyt2hM{N5oDEsZuyzM z@X!8J=f2jkggl-_8da|8n_1tvY4toPBS~jFdg}9Gk&A8nc*Hz`g?}u{&Z<3xFx~s(pkNk@PI3nzVI6pLsRt3Vb{_rcwAKWCqy>kovi7R&+XB|@O~Fg6tFf)t zhBg{WO7`VTZ~QcT{`dhkeNmDm_$H|*=YqV9Ojk$hk2fgA20^|Fr%ad#IC*wrXnuL- z0Hs?mRxCQCLew{mlEjVHTL0 zu}(gGO68oh0m-l8#|1$vt6=9_#PT~w7eMD^eoWUc7uQ;1 zMIh;8mKkZV$9O@6vEm&nVA(UhSu{$Lp!f8fILt8w?{7sccd%10x)`~T2zA^=jRm`l zW?+tm;gM4+rK4|JYE6pt#~A(oF9=c!XNd@Zu2j8{YR{1-M}YfUA-b0kJ2S4{ly^>w zFS14w^p$EKU{@QJJl2=o0=70#A7HPqj-aXqUOxOYxXsnQ^QWkFjo*#ug9k>Px<9`% zE1?Jj6F}jI8SN0h`#nFJ#ls&&3y0r3aKPHJ*xpFRbI27iWECrP5~g;aw)z(EL`-B z_1i4Jls<~b@nL=(i_Nyeo^~v}GP=@Vfqz7*aZCr&k(GTOyDO{(jj5$(R#ef4aQ&V{ zf%kQVcg+?=PA5|%{XRMwZ4B)aU@OdW3RL=C&* zZp9v)8{Zk#C4c8Uq1Zp}J-ZrPT_Ll1lDHN2EtrknL?teCv;8(yEfDje*M^xj#BXw} zy)!uGjld!(zj@?OaGl~7^4{9W7pB|2u@=4+fb*jnUT#joal(o={gj_VPd`)y5ZPHH z_-wkZW(>4uOXp~M-RPuUutqOW$e(gwAhFj0s6;?Qh z)n7ImxfdGrjU|{AUdebGLfUlQo4!n!?O5dK}5@dOa+Z+n&Erk;BeZ^92 z{#NT4?O7%qINxexb-{2Ivo?_;!Fac(*j^QnW6}DE(e9vQ^&3iCBTBP5TAO+8DXOew z(kVhmBRLQ}4>8#MB)u?%^v5jPx)2-gvCR!a2&HwWY%!Jf zeq$BE%d)~3UEiLriDX1aGJ4$0@IRM-4q4w-|25OTY(3{W9y`lw0Njy?5Q0uHw5pqA^ z&Jwh`#TTkAZ7>hviGW1fFpkRRFup$3LnQ0$PW#FYh=5})8^rhapkcU zymoW$1%i;+8}@fBF5WWI8t zh6RvX6Xb?!KTFyujPoRM&&NC0`){r<7kkKmn;Sy)Jon&9ipHX1%a5ris~h(}=N7C{ zkJEDSzr=eg{9Ep5UaiOAoj?u(av)$t$Lh_8_i1V4(^tWnr$2{U%fIe*|KgtYDN=RZ zAtUvI)v2j#0z9XUw|ZKBkD{UAEZ{46nbT?F7A`@~80imW;Ev14W;>@gM@9e8aT z+q`^w(@0B9BXMMLY4qjR%32pZzMk3)w4idy$=HRsULGg4aw`EVzq zgMmIiJ{e^{A<^elT{=8q6CYh>#W@S&MaEgW#UYfH>YUs~TkcBL`$VV!#JV16=zJ3(UQipq^S5=YlKGtLZAw1P)z<%GO4t# zFr+0`+=Zyu951An_ts)w#?ZmV_7nJY+Uz~fR_L`C`+4j1Hf#)Fi(&_Kai6?4LVFW5 z?`{2Y*zu{#AKm*9)6EfHT0|ijlXYLOQg}3$cAUQ^1@w^vrOwBE*PN2lj%s#yZ%uuB zBn|g5Qf8+6i@t5zqpK2H}~ijwu066CYq}@UO++)D-#Y%u-5wFt zZ}c1JbB5Q&>zdF{)R{%!d^4%uDK_Zc2w@LZ&fKu&5;$~}`emWc|Ab4W(()vsEGran zvy`QH*8F&JSlH^%W)qe5)0dRy0C)DXduI$Pc5!dI_=^A_UBgR#ee}85`PwA77@Phy z`n=H&#}9sES8L4+EruyclF^latvdd)Cf>DiL3S|@##52DFy6t=E)$QTF}1|tGg+O> z4Zv)5DFjoy=INgDr~jPz^Pn)53(W!saIdd6WDB7^U!7> z**^z>T_(UO!>UrB_6jOkUN5=x<+b(3<|ZTD3rP-0*(QVfQNB{Jg+@VojK*9kn}Nj> z5s=Z2>yn4LtASN-;P7|8{(SQi&p>J@5}gFn@SFVEDrz_H?Cl5G2}K1cC5#OTz=K%? zZY9jU-CR76GPO%RQvh%zZjW0zpMmo!vjKCEmo@u)vL zU&`D!eu;Aa)^H#Wx_DC6{PgW}b^z?APb8f+5JIC~hdtQ(dbafNL~PL03o7T%En<@F z!%&}$+;*=YHgcFKf0T^pRF5Bzs`NqlqY(|CZhtRlv?;m*K%8yQr<^_5D4`VC z9ExUR9jOZKjP($^Vlyr_8^hY=|r(@P{OWu&#GYmdjD2PsBrAH0IRRwlc?gWt1GPklHRR1&hW(Cq2>Iz2Eb z_O=%#Yw1Adw0YekKf6(B3;EVIRtzlb{N3or{kC$;k?*`3IujK+Hsl}gF*E(4srcE%2pvob%;n zTx;ToL=c9my&Y8rHjpim;$arKq92%0Jk#!h;ukMz@qnn(*xO>W;wn*?HD~YuuNG$w zZ%Zw`BKuIIOATJWaQNg#n0sG-0*tnY{=sL#?f_Eo&$B0HdY_zoVsKlr?*7NvkDN7u z!yTi9$!*6yzz02S;90+vG7282xlgw+UlSvK+e_n<+xGI*i*(HtN142w+0|Eva6fQL z)lJyIIq(Bgg}#jxUh`vklx8(f-rc6Eq+O&+_UvZWx2IBe9{X#k>-|WZ52g)``KI;=U>+NU z0*AqNWcqYo-5(1Ukm8KnZ#9N7Q>fL?UFWWTIOHh4Kz@T1Z95eA1H&cQ2qRuI?~ z>6>i_EVl_xvbGd=_O@SfcF)BIzV4qH4Vi5Ft#NyGUKIh9Wuem(rafobUUYv5KpDzP z3v$=>yue=>tOrRd3|^g z-}-v#{#dCqX9OKoPCKTDZ$Xi3Kc%$RUWKyIRR3MFb`Lbg_Xr8Zx>claErq8BJ-eh$ z3Sq#gzH3a?&3fjPaD@AwY;(Ij<5NeEWD4GYL4AD95mfjs)cR=V$Xc`E{1>GoO0HT& zF}}0%rNQ%;CFB`N*bS$oeJS^b#Wj8+XC&c0M)HpgBNB)kHE2d1?iDsxYpa@_5`0)x z{qsGMw>*rWxinfJx!Sq#l%m$g{CCB}kb>SO<6f#h|psLpmz}P1nE770^`e==d1v zu5vI!l`nqJ9wMKFZnaAn%R}=th6!-x9D%W&bYhji_MiUszH#c|qr#l&2$4NwKQAok zhAsCcukdo_c@+lfaRJ^Qq8+S4m=9?>!PKCy8v*eVaRF@9v4K+i%)Pz9`ul6xk(JeGwyV2} z;5o*fvlN*&Jz)IH5DK3Zb$cBay7-8}*CYaIhYe4pgpW4Kr-({hKLWCF9B@nmok;2O zUQeA7T<3y0wCz>pJM~A!s|fFd9%phbnfy|p-oJ1;rOReRd6(xl^HSv8xG54t->bj! zDui<%$R#~juUf`sGOqoO<3gSE=c_Z{~6^@TKqE=$D#7I$J7%Q z7;%g(gHZPURp(gjpY0-%)#02?78)8s<$KKM^^9@6NtAV4aCXQ2W3cZ5uBs}De;Tc^ zZ|JMJ?p8TJZ})%_?dkV=;@M0?O)n5D`wh==KiXfj+<<8*E#6qc*XB59cS6$N%&lp3Scp zKJGqA!yMZX!LtSN%Rf^I*#^Q$zNobq20;X6#y%Cd7o%&DT{{9hES#RW+!JVxu1p-*ml3zkj(QE3md*?=y$W*Q_;Q2 zh`DKPBm?~Rgs+ih-mMYn4DqVRQVw?^4xLrxT@}`kUVfkv_l^%GsS+)SPwAiZ3MCm< zv=^*2mWI=eaaw^+_5D$_qKAhOao`D3@^cNxZV`fi@1jqM{8_F>CV#~-dG@X1W1aZ% zaceW@7>Es`S-ti`w=ESl_2`yb3BQh7gu1{r%l+3x9vCO?B~JC^pq`rwxGWrB<}P*V zNcDzmy!4Xy8znz~^i*&Hh!q$U$ly$+Z*}eC6}wxy19wLGn=T0fA)lGi6Za4Nw#Q-L zE_v-gqRQoM%8%#^VM;NpFRPzO8;T7@A z>4=uLU28qOiAHUQG@!sE?fsf?VJ#;$hB>7@|6ab}~M$ShOHSYSybjhI9#_c_ldjxX* z+1rR5d6lN>hlJ86!sh!!wyw=K`nKK@(zykiImusH(Sm(}~FMQ-p7Nk4wsAoAq; zRrYz$rz2{a8rr-}Zf#pX5%Mr-hL8Q!j=j`qpt?yM6s9niL9zny=~;* z$#>w$JfW?ApJe#wy>Ti>I;4eD3NW7xG>$w?Z|8q$rn5IAEM62J+;o7c%-q$yb5Emh zLzQKPGm3S1G-@qk<&+Zfo%c6zC2wdwsEsgAexi5jj9^C6E1{=fn=#)Bthf`U62<3q zPekgZeT73^h7EAKT(il+_oe<1d+!<4mhi# zCtgMzmB3KmBnSo@Nw8W`!}sn^Jcq3jp;F;IQC7lmM&+k!_0LiCBV_?XVf$OpK0!H* zl*}l;wWsHuTWPGgbbZGIaNnFA=2VwM>b#Bv$UeuDa9czZZ!Fu z@ZsF&5MS61S)w-Gj(Uz~t`(@Qt0K<6O9WrP4TZt@y8bbRM)mecpfB`fL0lCD=m(j*}RgD;4)>svh3fMOrZ;sJy7AGl(og zD64pK9pP7v;*Pf6H`dG%@6<`Zw}`x!Z%3wL@0* zbNh-XVD4vcQ5W8sA@O1B99DS6u+`90tPOK|_yG<|D=L)ZM(Znbv&1ffnsG{k_FKa$ zL8OP@Wy0~7vVbL39seahuA&}!yPl8ApF(#%YrLH+RgGDO4k?VxOs^T4uKqe0^7&a( z-(7Mcv3*L~lHDGS#AJ%1JpFkST0G8bt1z%=uNmCeibm0PPThb-izK~Oml%}|;zGTS zkjh%a0wZNf6WyTn=_*#=ObqyIzjo@tt4iE@fs7=P%lhKxdTgIi;Aq4)d?LFAa+3w@rN0<2RLC)5)1vH4V{S40q9ghm5;BUo*p4dmtG2jWK;5 z0x3vDVv@6Q*F7R$C1(V*(9a8>eY_XsGItP`&d7HRlxjZ2Yf;x{JG%mmegyz>rDehq zDHWr3OG=8i8L|BOVPV7>MkQGifUt^;-8UKT%^XUt5czVXQ^AS zDN}pQky2u>yki5YsBBqpp@=kz5spxXeBQ=Aq8_S{lgFR276s-Haq6Khf+*c>(dY1K zb&&BLyNAy+s&0)u>~$Tf`}m;Cy6ZGe9+DX$LbVAzahjDFPBYyRv?qC7=b>uuA7L_T z-*yuecbR4C_(pd;Y;t?!x_r8wZ7r)tbIh@&XAcSQu#z{=cPy46+*F`CVCA-05Z7{nIQ zw$N`l-XC*r?q)}K7|TkwB2$G|o20)6^FhROF&=^2)30`eyt-&7BZz2WB=@X2iSeAXSr>Xnv)lJ!aJ#BN|0-=OS~>~sNr@VoZ1r2C~47%9LiTQH4oO& zQ-+e7MkCB0(D>By7P9Sy)n;OG$ndt8vZC108lQ^P099qSy#$MfcFufq#GU=+ zdJ%1yoG~BJq3XkYBiMFxd01-Ypc@^pE~BpKf~pB1n9{9S)n3g#Z8(bjbIA)hDgPvdRW!{_)c{6V`RcYb=u)m$ z{OQ5UCx#$-`w!Q$v)=ps^7!^(N={jJ0}chGL|4u8mM#{3k*dIj9^gXnilbz{j}xFT zAealo7K7sB_Flt=&D!E9{t*5yT~Ik6xVz7ErOQ1-@H(o(%kP`JlWd1qtEaC|R#|`p zGAoxEZj;5)7T+xP%uHCMusBKcdN%;DP-FIQR@QgBv+OdpF`ho05}_@NXhm|Z`yeIX z?bq~#Q#01_qJaCQf4gX=)N3Q8Z9|6YlfDdD*G5q{zFNlo_qTzWGl^=!e_U;y5=9k? z&<}mr$2*?63sHKV2`{LFWk0;CHvxoHv{QY;ia+#OOFL(do)5PF6>iJ-^9=*-5Djly zL-h6b^QW5YbQMWYPIPD{G!IeXZ6CMKZ%xU0mAnF7ma_M398_bCN^KGGxM$6bm80VL zM0;8eDJaep=X$ie$%t)_zEvU{hh&CibL_}8vRlNiP0Y!t@M4@)cN!DquOFS>RGP_D zVC=uj3WE?IAXG%qJopM{a-RhC(%u?uxENK92J?jtL<<|zvtG{Oc_x~S|n%Z{BL zHC|UcML$lKx#?0XLVJV@s}L4PG4}>`3pRl7YVhRBZplCY#VAu=1{5)d#~G4F{K43~#0)=~Y1H2s+X$bMl-EXOlrU;4tI3M3==^C$$-bdC^UM*%kbR*k^}@Ub>X8`q>eoX^8^dPi z_`B`kR__p5M&rDw#alOiS8_*B13p| zeHGb@***vEvN>P3Rv%4F`^*LHOx{iQ>=>#$l4g|-^rZE&3Wl54SIZp48Q1yOPy~(v z)!8KY`$G?Ll>P^L&JotAtEZ6a`X1xyumM7eZ9FB-CgG~OzXbW*{=4fJ;ZaIFEygKC zl5Z!Rt*LTQ5L*?LEP+jL$OU!l5yVThE#ii^@pn<%se*n$scr1evs0?Mmmpa63kv zr`~KH4P)S1noY??(5YanxBw}|hvn-mST6=g;N-^ryv2e9?-nHod!---fX7Y**Y>RV zQk#PsoY{3%BZ($vrFNe>Q?<3I8DPW6GiWV27Anw;Y1`Zd|LrL`#1-{-XFKo7Gr6Dl zA#$=p%kTYlwJGLXL!XgLcyDW5K`!H}2_N?9zVqz((=?p$i9ZyhEUtSq0U4(*tBh=1 zJOtMsUvhvUUb#mfpxk)a--086`^^xC-Soal>F6>-az=e*x#aF48aciaYXNpXxU=mk z+r5j}4P|){JhfZ6f`CtE(Um_R5j4K#SCW@fH@4-Z9W|Puznn?@ZGN{% zOTdPMKzF|xWoiGls6Qk>;mEnc2|)1#UF?}ZMV;U7Cdd0;h)tt zLz2k_mOKBsB>teo7*WFj;$4wEb#_y)X09znWgDQSQ>Yx*KYo$K#)Jd9har$t zbhhKm-4OBx=F3<^@zIs$%_)`)Evo2*kV%1MLE0FaGm}13fihQVyOH;&yP3sU`Dm26 z8h-%_j+?;qCD4RL_=?x=1j%oI2=({pe;@=au_)l}2&}jrz4neE9`nHjf^|o&Mi4~= zmyAN7)4G2cgCye)@?q5X;tdplru(3(W^Q zb~LjgMBT+)k28?Hw>q<#k_hwejZ`faFOL+uJ?;{7WBG=OM=L$5Zy+&N05&iD9f?Ar zRvO$4C1n!;Ov?X)09cXz&`U${7-7{2G^DKztRr(&%�}iIS<*>u6 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%0AYm>?pG`}5RzTbox-P+ne998;e? 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