+
diff --git a/src/plugins/timelion/public/partials/save_sheet.html b/src/plugins/timelion/public/partials/save_sheet.html
index a0e0727f3ec82..7773a9d25df71 100644
--- a/src/plugins/timelion/public/partials/save_sheet.html
+++ b/src/plugins/timelion/public/partials/save_sheet.html
@@ -19,7 +19,7 @@
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
index d15825718682c..1bf5039ef05fa 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx
@@ -993,9 +993,11 @@ describe('IndexPatternDimensionEditorPanel', () => {
'Average',
'Count',
'Maximum',
+ 'Median',
'Minimum',
'Sum',
'Unique count',
+ '\u00a0',
]);
});
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
index 38aec866ca5cb..735015492bd5a 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/index.ts
@@ -18,6 +18,8 @@ import {
SumIndexPatternColumn,
maxOperation,
MaxIndexPatternColumn,
+ medianOperation,
+ MedianIndexPatternColumn,
} from './metrics';
import { dateHistogramOperation, DateHistogramIndexPatternColumn } from './date_histogram';
import { countOperation, CountIndexPatternColumn } from './count';
@@ -43,6 +45,7 @@ export type IndexPatternColumn =
| AvgIndexPatternColumn
| CardinalityIndexPatternColumn
| SumIndexPatternColumn
+ | MedianIndexPatternColumn
| CountIndexPatternColumn;
export type FieldBasedIndexPatternColumn = Extract
;
@@ -59,6 +62,7 @@ const internalOperationDefinitions = [
averageOperation,
cardinalityOperation,
sumOperation,
+ medianOperation,
countOperation,
rangeOperation,
];
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx
index c02f7bcb7d2cd..1d3ecc165ce74 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/metrics.tsx
@@ -87,6 +87,7 @@ export type SumIndexPatternColumn = MetricColumn<'sum'>;
export type AvgIndexPatternColumn = MetricColumn<'avg'>;
export type MinIndexPatternColumn = MetricColumn<'min'>;
export type MaxIndexPatternColumn = MetricColumn<'max'>;
+export type MedianIndexPatternColumn = MetricColumn<'median'>;
export const minOperation = buildMetricOperation({
type: 'min',
@@ -137,3 +138,15 @@ export const sumOperation = buildMetricOperation({
values: { name },
}),
});
+
+export const medianOperation = buildMetricOperation({
+ type: 'median',
+ displayName: i18n.translate('xpack.lens.indexPattern.median', {
+ defaultMessage: 'Median',
+ }),
+ ofName: (name) =>
+ i18n.translate('xpack.lens.indexPattern.medianOf', {
+ defaultMessage: 'Median of {name}',
+ values: { name },
+ }),
+});
diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts
index c1bd4b84099b7..6808bc724f26b 100644
--- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts
+++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/operations.test.ts
@@ -315,12 +315,12 @@ describe('getOperationTypesForField', () => {
},
Object {
"field": "bytes",
- "operationType": "min",
+ "operationType": "max",
"type": "field",
},
Object {
"field": "bytes",
- "operationType": "max",
+ "operationType": "min",
"type": "field",
},
Object {
@@ -338,6 +338,11 @@ describe('getOperationTypesForField', () => {
"operationType": "cardinality",
"type": "field",
},
+ Object {
+ "field": "bytes",
+ "operationType": "median",
+ "type": "field",
+ },
],
},
]
diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js
index be214e3b01e67..813d01ff90861 100644
--- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js
+++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js
@@ -175,6 +175,16 @@ export function convertESShapeToGeojsonGeometry(value) {
geoJson.type = GEO_JSON_TYPE.GEOMETRY_COLLECTION;
break;
case 'envelope':
+ // format defined here https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html#_envelope
+ const polygon = formatEnvelopeAsPolygon({
+ minLon: geoJson.coordinates[0][0],
+ maxLon: geoJson.coordinates[1][0],
+ minLat: geoJson.coordinates[1][1],
+ maxLat: geoJson.coordinates[0][1],
+ });
+ geoJson.type = polygon.type;
+ geoJson.coordinates = polygon.coordinates;
+ break;
case 'circle':
const errorMessage = i18n.translate(
'xpack.maps.es_geo_utils.convert.unsupportedGeometryTypeErrorMessage',
diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js
index a8d5d650740cd..ccab57dd18339 100644
--- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js
+++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js
@@ -250,6 +250,30 @@ describe('geoShapeToGeometry', () => {
expect(shapes[0].coordinates).toEqual(coordinates);
});
+ it('Should convert envelope to geojson', () => {
+ const coordinates = [
+ [100.0, 1.0],
+ [101.0, 0.0],
+ ];
+ const value = {
+ type: 'envelope',
+ coordinates: coordinates,
+ };
+ const shapes = [];
+ geoShapeToGeometry(value, shapes);
+ expect(shapes.length).toBe(1);
+ expect(shapes[0].type).toBe('Polygon');
+ expect(shapes[0].coordinates).toEqual([
+ [
+ [100, 1],
+ [100, 0],
+ [101, 0],
+ [101, 1],
+ [100, 1],
+ ],
+ ]);
+ });
+
it('Should convert array of values', () => {
const linestringCoordinates = [
[-77.03653, 38.897676],
diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx
index b97ddb2690982..08b2d48a982d6 100644
--- a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx
+++ b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui';
@@ -93,10 +93,8 @@ export const useDataGrid = (
[columns]
);
- return {
- chartsVisible,
- chartsButtonVisible: true,
- columnsWithCharts: columns.map((c, index) => {
+ const columnsWithCharts = useMemo(() => {
+ const updatedColumns = columns.map((c, index) => {
const chartData = columnCharts.find((cd) => cd.id === c.id);
return {
@@ -110,7 +108,32 @@ export const useDataGrid = (
/>
) : undefined,
};
- }),
+ });
+
+ // Sort the columns to be in line with the current order of visible columns.
+ // EuiDataGrid misses a callback for the order of all available columns, so
+ // this only can retain the order of visible columns.
+ return updatedColumns.sort((a, b) => {
+ // This will always move visible columns above invisible ones.
+ if (visibleColumns.indexOf(a.id) === -1 && visibleColumns.indexOf(b.id) > -1) {
+ return 1;
+ }
+ if (visibleColumns.indexOf(b.id) === -1 && visibleColumns.indexOf(a.id) > -1) {
+ return -1;
+ }
+ if (visibleColumns.indexOf(a.id) === -1 && visibleColumns.indexOf(b.id) === -1) {
+ return a.id.localeCompare(b.id);
+ }
+
+ // If both columns are visible sort by their visible sorting order.
+ return visibleColumns.indexOf(a.id) - visibleColumns.indexOf(b.id);
+ });
+ }, [columns, columnCharts, chartsVisible, JSON.stringify(visibleColumns)]);
+
+ return {
+ chartsVisible,
+ chartsButtonVisible: true,
+ columnsWithCharts,
errorMessage,
invalidSortingColumnns,
noDataMessage,
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx
index 06bcdfd364d66..c837fcbacdd55 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx
@@ -149,11 +149,11 @@ export const ExplorationQueryBar: FC = ({
placeholder={
searchInput.language === SEARCH_QUERY_LANGUAGE.KUERY
? i18n.translate('xpack.ml.stepDefineForm.queryPlaceholderKql', {
- defaultMessage: 'e.g. {example}',
+ defaultMessage: 'Search for e.g. {example}',
values: { example: 'method : "GET" or status : "404"' },
})
: i18n.translate('xpack.ml.stepDefineForm.queryPlaceholderLucene', {
- defaultMessage: 'e.g. {example}',
+ defaultMessage: 'Search for e.g. {example}',
values: { example: 'method:GET OR status:404' },
})
}
diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts
index eded8e82a7919..88aa06808e8a7 100644
--- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts
+++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts
@@ -52,17 +52,21 @@ export const useOutlierData = (
const needsDestIndexFields =
indexPattern !== undefined && indexPattern.title === jobConfig?.source.index[0];
- const columns: EuiDataGridColumn[] = [];
-
- if (jobConfig !== undefined && indexPattern !== undefined) {
- const resultsField = jobConfig.dest.results_field;
- const { fieldTypes } = getIndexFields(jobConfig, needsDestIndexFields);
- columns.push(
- ...getDataGridSchemasFromFieldTypes(fieldTypes, resultsField).sort((a: any, b: any) =>
- sortExplorationResultsFields(a.id, b.id, jobConfig)
- )
- );
- }
+ const columns = useMemo(() => {
+ const newColumns: EuiDataGridColumn[] = [];
+
+ if (jobConfig !== undefined && indexPattern !== undefined) {
+ const resultsField = jobConfig.dest.results_field;
+ const { fieldTypes } = getIndexFields(jobConfig, needsDestIndexFields);
+ newColumns.push(
+ ...getDataGridSchemasFromFieldTypes(fieldTypes, resultsField).sort((a: any, b: any) =>
+ sortExplorationResultsFields(a.id, b.id, jobConfig)
+ )
+ );
+ }
+
+ return newColumns;
+ }, [jobConfig, indexPattern]);
const dataGrid = useDataGrid(
columns,
@@ -124,7 +128,10 @@ export const useOutlierData = (
}, [
dataGrid.chartsVisible,
jobConfig?.dest.index,
- JSON.stringify([searchQuery, dataGrid.visibleColumns]),
+ // Only trigger when search or the visible columns changes.
+ // We're only interested in the visible columns but not their order, that's
+ // why we sort for comparison (and copying it via spread to avoid sort in place).
+ JSON.stringify([searchQuery, [...dataGrid.visibleColumns].sort()]),
]);
const colorRange = useColorRange(
diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts
index fdd7253550624..824eeab7245b4 100644
--- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts
+++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts
@@ -7,12 +7,16 @@
import { fetchStatus } from './fetch_status';
import { AlertUiState, AlertState } from '../../alerts/types';
import { AlertSeverity } from '../../../common/enums';
-import { ALERT_CPU_USAGE, ALERT_CLUSTER_HEALTH } from '../../../common/constants';
+import {
+ ALERT_CPU_USAGE,
+ ALERT_CLUSTER_HEALTH,
+ ALERT_DISK_USAGE,
+ ALERT_MISSING_MONITORING_DATA,
+} from '../../../common/constants';
describe('fetchStatus', () => {
const alertType = ALERT_CPU_USAGE;
const alertTypes = [alertType];
- const log = { warn: jest.fn() };
const start = 0;
const end = 0;
const id = 1;
@@ -53,6 +57,7 @@ describe('fetchStatus', () => {
afterEach(() => {
(alertsClient.find as jest.Mock).mockClear();
(alertsClient.getAlertState as jest.Mock).mockClear();
+ alertStates.length = 0;
});
it('should fetch from the alerts client', async () => {
@@ -62,8 +67,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect(status).toEqual({
monitoring_alert_cpu_usage: {
@@ -98,8 +102,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect(Object.values(status).length).toBe(1);
expect(Object.keys(status)).toEqual(alertTypes);
@@ -126,8 +129,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
customStart,
- customEnd,
- log as any
+ customEnd
);
expect(Object.values(status).length).toBe(1);
expect(Object.keys(status)).toEqual(alertTypes);
@@ -141,8 +143,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect((alertsClient.find as jest.Mock).mock.calls[0][0].options.filter).toBe(
`alert.attributes.alertTypeId:${alertType}`
@@ -160,8 +161,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect(status[alertType].states.length).toEqual(0);
});
@@ -178,8 +178,7 @@ describe('fetchStatus', () => {
alertTypes,
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect(status).toEqual({});
});
@@ -197,9 +196,51 @@ describe('fetchStatus', () => {
[ALERT_CLUSTER_HEALTH],
defaultClusterState.clusterUuid,
start,
- end,
- log as any
+ end
);
expect(customLicenseService.getWatcherFeature).toHaveBeenCalled();
});
+
+ it('should sort the alerts', async () => {
+ const customAlertsClient = {
+ find: jest.fn(() => ({
+ total: 1,
+ data: [
+ {
+ id,
+ },
+ ],
+ })),
+ getAlertState: jest.fn(() => ({
+ alertInstances: {
+ abc: {
+ state: {
+ alertStates: [
+ {
+ cluster: defaultClusterState,
+ ui: {
+ ...defaultUiState,
+ isFiring: true,
+ },
+ },
+ ],
+ },
+ },
+ },
+ })),
+ };
+ const status = await fetchStatus(
+ customAlertsClient as any,
+ licenseService as any,
+ [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA],
+ defaultClusterState.clusterUuid,
+ start,
+ end
+ );
+ expect(Object.keys(status)).toEqual([
+ ALERT_CPU_USAGE,
+ ALERT_DISK_USAGE,
+ ALERT_MISSING_MONITORING_DATA,
+ ]);
+ });
});
diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts
index 49e688fafbee5..ed49f42e4908c 100644
--- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts
+++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts
@@ -18,8 +18,9 @@ export async function fetchStatus(
clusterUuid: string,
start: number,
end: number,
- filters: CommonAlertFilter[]
+ filters: CommonAlertFilter[] = []
): Promise<{ [type: string]: CommonAlertStatus }> {
+ const types: Array<{ type: string; result: CommonAlertStatus }> = [];
const byType: { [type: string]: CommonAlertStatus } = {};
await Promise.all(
(alertTypes || ALERTS).map(async (type) => {
@@ -39,7 +40,7 @@ export async function fetchStatus(
alert: serialized,
};
- byType[type] = result;
+ types.push({ type, result });
const id = alert.getId();
if (!id) {
@@ -75,5 +76,10 @@ export async function fetchStatus(
})
);
+ types.sort((a, b) => (a.type === b.type ? 0 : a.type.length > b.type.length ? 1 : -1));
+ for (const { type, result } of types) {
+ byType[type] = result;
+ }
+
return byType;
}
diff --git a/x-pack/plugins/reporting/public/components/panel_spinner.tsx b/x-pack/plugins/reporting/public/components/panel_spinner.tsx
new file mode 100644
index 0000000000000..841b7063361b9
--- /dev/null
+++ b/x-pack/plugins/reporting/public/components/panel_spinner.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
+
+export const PanelSpinner: React.FC = (props) => {
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
index 22b97f45db186..18895f9e623eb 100644
--- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
+++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx
@@ -13,7 +13,7 @@ import { toMountPoint } from '../../../../../src/plugins/kibana_react/public';
import { BaseParams } from '../../common/types';
import { ReportingAPIClient } from '../lib/reporting_api_client';
-interface Props {
+export interface Props {
apiClient: ReportingAPIClient;
toasts: ToastsSetup;
reportType: string;
diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx
new file mode 100644
index 0000000000000..45a7d60a60966
--- /dev/null
+++ b/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { lazy, Suspense, FC } from 'react';
+import { PanelSpinner } from './panel_spinner';
+import type { Props } from './reporting_panel_content';
+
+const LazyComponent = lazy(() =>
+ import('./reporting_panel_content').then(({ ReportingPanelContent }) => ({
+ default: ReportingPanelContent,
+ }))
+);
+
+export const ReportingPanelContent: FC> = (props) => {
+ return (
+ }>
+
+
+ );
+};
diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx
index 4a62ab2b76508..ff81ced43e0b4 100644
--- a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx
+++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx
@@ -12,7 +12,7 @@ import { BaseParams } from '../../common/types';
import { ReportingAPIClient } from '../lib/reporting_api_client';
import { ReportingPanelContent } from './reporting_panel_content';
-interface Props {
+export interface Props {
apiClient: ReportingAPIClient;
toasts: ToastsSetup;
reportType: string;
diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx
new file mode 100644
index 0000000000000..52080e16dd6a3
--- /dev/null
+++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx
@@ -0,0 +1,24 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { lazy, Suspense, FC } from 'react';
+import { PanelSpinner } from './panel_spinner';
+import type { Props } from './screen_capture_panel_content';
+
+const LazyComponent = lazy(() =>
+ import('./screen_capture_panel_content').then(({ ScreenCapturePanelContent }) => ({
+ default: ScreenCapturePanelContent,
+ }))
+);
+
+export const ScreenCapturePanelContent: FC = (props) => {
+ return (
+ }>
+
+
+ );
+};
diff --git a/x-pack/plugins/reporting/public/mount_management_section.tsx b/x-pack/plugins/reporting/public/mount_management_section.tsx
new file mode 100644
index 0000000000000..ac737e4a318ac
--- /dev/null
+++ b/x-pack/plugins/reporting/public/mount_management_section.tsx
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import * as React from 'react';
+import { render, unmountComponentAtNode } from 'react-dom';
+import { I18nProvider } from '@kbn/i18n/react';
+import { CoreSetup, CoreStart } from 'src/core/public';
+import { Observable } from 'rxjs';
+import { ReportListing } from './components/report_listing';
+import { ManagementAppMountParams } from '../../../../src/plugins/management/public';
+import { ILicense } from '../../licensing/public';
+import { ClientConfigType } from './plugin';
+import { ReportingAPIClient } from './lib/reporting_api_client';
+
+export async function mountManagementSection(
+ coreSetup: CoreSetup,
+ coreStart: CoreStart,
+ license$: Observable,
+ pollConfig: ClientConfigType['poll'],
+ apiClient: ReportingAPIClient,
+ params: ManagementAppMountParams
+) {
+ render(
+
+
+ ,
+ params.element
+ );
+
+ return () => {
+ unmountComponentAtNode(params.element);
+ };
+}
diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.ts
similarity index 78%
rename from x-pack/plugins/reporting/public/plugin.tsx
rename to x-pack/plugins/reporting/public/plugin.ts
index cc5964f737988..33f4fd4abf72c 100644
--- a/x-pack/plugins/reporting/public/plugin.tsx
+++ b/x-pack/plugins/reporting/public/plugin.ts
@@ -5,9 +5,6 @@
*/
import { i18n } from '@kbn/i18n';
-import { I18nProvider } from '@kbn/i18n/react';
-import React from 'react';
-import ReactDOM from 'react-dom';
import * as Rx from 'rxjs';
import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators';
import {
@@ -17,21 +14,21 @@ import {
Plugin,
PluginInitializerContext,
} from 'src/core/public';
-import { UiActionsSetup } from 'src/plugins/ui_actions/public';
+import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public';
import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public';
import {
FeatureCatalogueCategory,
HomePublicPluginSetup,
+ HomePublicPluginStart,
} from '../../../../src/plugins/home/public';
-import { ManagementSetup } from '../../../../src/plugins/management/public';
-import { SharePluginSetup } from '../../../../src/plugins/share/public';
-import { LicensingPluginSetup } from '../../licensing/public';
+import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public';
+import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public';
+import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public';
import { durationToNumber } from '../common/schema_utils';
import { JobId, ReportingConfigType } from '../common/types';
import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../constants';
import { JobSummarySet } from './';
import { getGeneralErrorToast } from './components';
-import { ReportListing } from './components/report_listing';
import { ReportingAPIClient } from './lib/reporting_api_client';
import { ReportingNotifierStreamHandler as StreamHandler } from './lib/stream_handler';
import { GetCsvReportPanelAction } from './panel_actions/get_csv_panel_action';
@@ -60,7 +57,25 @@ function handleError(notifications: NotificationsSetup, err: Error): Rx.Observab
return Rx.of({ completed: [], failed: [] });
}
-export class ReportingPublicPlugin implements Plugin {
+export interface ReportingPublicPluginSetupDendencies {
+ home: HomePublicPluginSetup;
+ management: ManagementSetup;
+ licensing: LicensingPluginSetup;
+ uiActions: UiActionsSetup;
+ share: SharePluginSetup;
+}
+
+export interface ReportingPublicPluginStartDendencies {
+ home: HomePublicPluginStart;
+ management: ManagementStart;
+ licensing: LicensingPluginStart;
+ uiActions: UiActionsStart;
+ share: SharePluginStart;
+}
+
+export class ReportingPublicPlugin
+ implements
+ Plugin {
private config: ClientConfigType;
private readonly stop$ = new Rx.ReplaySubject(1);
private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', {
@@ -76,19 +91,7 @@ export class ReportingPublicPlugin implements Plugin {
public setup(
core: CoreSetup,
- {
- home,
- management,
- licensing,
- uiActions,
- share,
- }: {
- home: HomePublicPluginSetup;
- management: ManagementSetup;
- licensing: LicensingPluginSetup;
- uiActions: UiActionsSetup;
- share: SharePluginSetup;
- }
+ { home, management, licensing, uiActions, share }: ReportingPublicPluginSetupDendencies
) {
const {
http,
@@ -119,24 +122,19 @@ export class ReportingPublicPlugin implements Plugin {
title: this.title,
order: 1,
mount: async (params) => {
- const [start] = await getStartServices();
params.setBreadcrumbs([{ text: this.breadcrumbText }]);
- ReactDOM.render(
-
-
- ,
- params.element
+ const [[start], { mountManagementSection }] = await Promise.all([
+ getStartServices(),
+ import('./mount_management_section'),
+ ]);
+ return await mountManagementSection(
+ core,
+ start,
+ license$,
+ this.config.poll,
+ apiClient,
+ params
);
-
- return () => {
- ReactDOM.unmountComponentAtNode(params.element);
- };
},
});
diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx
index 451d907199c4c..e90d6786b58f2 100644
--- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx
+++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx
@@ -11,7 +11,7 @@ import { IUiSettingsClient, ToastsSetup } from 'src/core/public';
import { ShareContext } from '../../../../../src/plugins/share/public';
import { LicensingPluginSetup } from '../../../licensing/public';
import { JobParamsCSV, SearchRequest } from '../../server/export_types/csv/types';
-import { ReportingPanelContent } from '../components/reporting_panel_content';
+import { ReportingPanelContent } from '../components/reporting_panel_content_lazy';
import { checkLicense } from '../lib/license_check';
import { ReportingAPIClient } from '../lib/reporting_api_client';
diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx
index 2dab66187bb25..d17d4af3c0102 100644
--- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx
+++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx
@@ -13,7 +13,7 @@ import { LicensingPluginSetup } from '../../../licensing/public';
import { LayoutParams } from '../../common/types';
import { JobParamsPNG } from '../../server/export_types/png/types';
import { JobParamsPDF } from '../../server/export_types/printable_pdf/types';
-import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content';
+import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content_lazy';
import { checkLicense } from '../lib/license_check';
import { ReportingAPIClient } from '../lib/reporting_api_client';
diff --git a/x-pack/plugins/reporting/server/config/schema.ts b/x-pack/plugins/reporting/server/config/schema.ts
index 8276e8b49d348..7a21c5a1f6104 100644
--- a/x-pack/plugins/reporting/server/config/schema.ts
+++ b/x-pack/plugins/reporting/server/config/schema.ts
@@ -45,7 +45,15 @@ const QueueSchema = schema.object({
const RulesSchema = schema.object({
allow: schema.boolean(),
host: schema.maybe(schema.string()),
- protocol: schema.maybe(schema.string()),
+ protocol: schema.maybe(
+ schema.string({
+ validate(value) {
+ if (!/:$/.test(value)) {
+ return 'must end in colon';
+ }
+ },
+ })
+ ),
});
const CaptureSchema = schema.object({
diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
index 7e3b3d125fb5d..66119e098238e 100644
--- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts
@@ -26,6 +26,53 @@ interface Node {
parent_entity_id?: string;
}
+describe('data generator data streams', () => {
+ // these tests cast the result of the generate methods so that we can specifically compare the `data_stream` fields
+ it('creates a generator with default data streams', () => {
+ const generator = new EndpointDocGenerator('seed');
+ expect(generator.generateHostMetadata().data_stream).toEqual({
+ type: 'metrics',
+ dataset: 'endpoint.metadata',
+ namespace: 'default',
+ });
+ expect(generator.generatePolicyResponse().data_stream).toEqual({
+ type: 'metrics',
+ dataset: 'endpoint.policy',
+ namespace: 'default',
+ });
+ expect(generator.generateEvent().data_stream).toEqual({
+ type: 'logs',
+ dataset: 'endpoint.events.process',
+ namespace: 'default',
+ });
+ expect(generator.generateAlert().data_stream).toEqual({
+ type: 'logs',
+ dataset: 'endpoint.alerts',
+ namespace: 'default',
+ });
+ });
+
+ it('creates a generator with custom data streams', () => {
+ const metadataDataStream = { type: 'meta', dataset: 'dataset', namespace: 'name' };
+ const policyDataStream = { type: 'policy', dataset: 'fake', namespace: 'something' };
+ const eventsDataStream = { type: 'events', dataset: 'events stuff', namespace: 'name' };
+ const alertsDataStream = { type: 'alerts', dataset: 'alerts stuff', namespace: 'name' };
+ const generator = new EndpointDocGenerator('seed');
+ expect(generator.generateHostMetadata(0, metadataDataStream).data_stream).toStrictEqual(
+ metadataDataStream
+ );
+ expect(generator.generatePolicyResponse({ policyDataStream }).data_stream).toStrictEqual(
+ policyDataStream
+ );
+ expect(generator.generateEvent({ eventsDataStream }).data_stream).toStrictEqual(
+ eventsDataStream
+ );
+ expect(generator.generateAlert({ alertsDataStream }).data_stream).toStrictEqual(
+ alertsDataStream
+ );
+ });
+});
+
describe('data generator', () => {
let generator: EndpointDocGenerator;
beforeEach(() => {
@@ -69,7 +116,7 @@ describe('data generator', () => {
it('creates policy response documents', () => {
const timestamp = new Date().getTime();
- const hostPolicyResponse = generator.generatePolicyResponse(timestamp);
+ const hostPolicyResponse = generator.generatePolicyResponse({ ts: timestamp });
expect(hostPolicyResponse['@timestamp']).toEqual(timestamp);
expect(hostPolicyResponse.event.created).toEqual(timestamp);
expect(hostPolicyResponse.Endpoint).not.toBeNull();
@@ -80,7 +127,7 @@ describe('data generator', () => {
it('creates alert event documents', () => {
const timestamp = new Date().getTime();
- const alert = generator.generateAlert(timestamp);
+ const alert = generator.generateAlert({ ts: timestamp });
expect(alert['@timestamp']).toEqual(timestamp);
expect(alert.event?.action).not.toBeNull();
expect(alert.Endpoint).not.toBeNull();
diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
index f0254616e6c9d..07b230ffc6cc5 100644
--- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts
@@ -7,6 +7,7 @@ import uuid from 'uuid';
import seedrandom from 'seedrandom';
import {
AlertEvent,
+ DataStream,
EndpointStatus,
Host,
HostMetadata,
@@ -59,6 +60,7 @@ interface EventOptions {
pid?: number;
parentPid?: number;
extensions?: object;
+ eventsDataStream?: DataStream;
}
const Windows: OSFields[] = [
@@ -330,6 +332,8 @@ export interface TreeOptions {
percentTerminated?: number;
alwaysGenMaxChildrenPerNode?: boolean;
ancestryArraySize?: number;
+ eventsDataStream?: DataStream;
+ alertsDataStream?: DataStream;
}
type TreeOptionDefaults = Required;
@@ -351,19 +355,51 @@ export function getTreeOptionsWithDef(options?: TreeOptions): TreeOptionDefaults
percentTerminated: options?.percentTerminated ?? 100,
alwaysGenMaxChildrenPerNode: options?.alwaysGenMaxChildrenPerNode ?? false,
ancestryArraySize: options?.ancestryArraySize ?? ANCESTRY_LIMIT,
+ eventsDataStream: options?.eventsDataStream ?? eventsDefaultDataStream,
+ alertsDataStream: options?.alertsDataStream ?? alertsDefaultDataStream,
};
}
+const metadataDefaultDataStream = {
+ type: 'metrics',
+ dataset: 'endpoint.metadata',
+ namespace: 'default',
+};
+
+const policyDefaultDataStream = {
+ type: 'metrics',
+ dataset: 'endpoint.policy',
+ namespace: 'default',
+};
+
+const eventsDefaultDataStream = {
+ type: 'logs',
+ dataset: 'endpoint.events.process',
+ namespace: 'default',
+};
+
+const alertsDefaultDataStream = {
+ type: 'logs',
+ dataset: 'endpoint.alerts',
+ namespace: 'default',
+};
+
export class EndpointDocGenerator {
commonInfo: HostInfo;
random: seedrandom.prng;
sequence: number = 0;
+ /**
+ * The EndpointDocGenerator parameters
+ *
+ * @param seed either a string to seed the random number generator or a random number generator function
+ */
constructor(seed: string | seedrandom.prng = Math.random().toString()) {
if (typeof seed === 'string') {
this.random = seedrandom(seed);
} else {
this.random = seed;
}
+
this.commonInfo = this.createHostData();
}
@@ -383,6 +419,21 @@ export class EndpointDocGenerator {
this.commonInfo.Endpoint.policy.applied.status = this.randomChoice(POLICY_RESPONSE_STATUSES);
}
+ /**
+ * Parses an index and returns the data stream fields extracted from the index.
+ *
+ * @param index the index name to parse into the data stream parts
+ */
+ public static createDataStreamFromIndex(index: string): DataStream {
+ // e.g. logs-endpoint.events.network-default
+ const parts = index.split('-');
+ return {
+ type: parts[0], // logs
+ dataset: parts[1], // endpoint.events.network
+ namespace: parts[2], // default
+ };
+ }
+
private createHostData(): HostInfo {
const hostName = this.randomHostname();
return {
@@ -417,8 +468,12 @@ export class EndpointDocGenerator {
/**
* Creates a host metadata document
* @param ts - Timestamp to put in the event
+ * @param metadataDataStream the values to populate the data_stream fields when generating metadata documents
*/
- public generateHostMetadata(ts = new Date().getTime()): HostMetadata {
+ public generateHostMetadata(
+ ts = new Date().getTime(),
+ metadataDataStream = metadataDefaultDataStream
+ ): HostMetadata {
return {
'@timestamp': ts,
event: {
@@ -432,6 +487,7 @@ export class EndpointDocGenerator {
dataset: 'endpoint.metadata',
},
...this.commonInfo,
+ data_stream: metadataDataStream,
};
}
@@ -441,15 +497,24 @@ export class EndpointDocGenerator {
* @param entityID - entityID of the originating process
* @param parentEntityID - optional entityID of the parent process, if it exists
* @param ancestry - an array of ancestors for the generated alert
+ * @param alertsDataStream the values to populate the data_stream fields when generating alert documents
*/
- public generateAlert(
+ public generateAlert({
ts = new Date().getTime(),
entityID = this.randomString(10),
- parentEntityID?: string,
- ancestry: string[] = []
- ): AlertEvent {
+ parentEntityID,
+ ancestry = [],
+ alertsDataStream = alertsDefaultDataStream,
+ }: {
+ ts?: number;
+ entityID?: string;
+ parentEntityID?: string;
+ ancestry?: string[];
+ alertsDataStream?: DataStream;
+ } = {}): AlertEvent {
return {
...this.commonInfo,
+ data_stream: alertsDataStream,
'@timestamp': ts,
ecs: {
version: '1.4.0',
@@ -598,6 +663,7 @@ export class EndpointDocGenerator {
return {};
})(options.eventCategory);
return {
+ data_stream: options?.eventsDataStream ?? eventsDefaultDataStream,
'@timestamp': options.timestamp ? options.timestamp : new Date().getTime(),
agent: { ...this.commonInfo.agent, type: 'endpoint' },
ecs: {
@@ -813,6 +879,7 @@ export class EndpointDocGenerator {
const startDate = new Date().getTime();
const root = this.generateEvent({
timestamp: startDate + 1000,
+ eventsDataStream: opts.eventsDataStream,
});
events.push(root);
let ancestor = root;
@@ -824,18 +891,24 @@ export class EndpointDocGenerator {
secBeforeAlert: number,
eventList: Event[]
) => {
- for (const relatedAlert of this.relatedAlertsGenerator(node, alertsPerNode, secBeforeAlert)) {
+ for (const relatedAlert of this.relatedAlertsGenerator({
+ node,
+ relatedAlerts: alertsPerNode,
+ alertCreationTime: secBeforeAlert,
+ alertsDataStream: opts.alertsDataStream,
+ })) {
eventList.push(relatedAlert);
}
};
const addRelatedEvents = (node: Event, secBeforeEvent: number, eventList: Event[]) => {
- for (const relatedEvent of this.relatedEventsGenerator(
+ for (const relatedEvent of this.relatedEventsGenerator({
node,
- opts.relatedEvents,
- secBeforeEvent,
- opts.relatedEventsOrdered
- )) {
+ relatedEvents: opts.relatedEvents,
+ processDuration: secBeforeEvent,
+ ordered: opts.relatedEventsOrdered,
+ eventsDataStream: opts.eventsDataStream,
+ })) {
eventList.push(relatedEvent);
}
};
@@ -857,6 +930,7 @@ export class EndpointDocGenerator {
parentEntityID: parentEntityIDSafeVersion(root),
eventCategory: ['process'],
eventType: ['end'],
+ eventsDataStream: opts.eventsDataStream,
})
);
}
@@ -877,6 +951,7 @@ export class EndpointDocGenerator {
ancestryArrayLimit: opts.ancestryArraySize,
parentPid: firstNonNullValue(ancestor.process?.pid),
pid: this.randomN(5000),
+ eventsDataStream: opts.eventsDataStream,
});
events.push(ancestor);
timestamp = timestamp + 1000;
@@ -892,6 +967,7 @@ export class EndpointDocGenerator {
eventType: ['end'],
ancestry: ancestryArray(ancestor),
ancestryArrayLimit: opts.ancestryArraySize,
+ eventsDataStream: opts.eventsDataStream,
})
);
}
@@ -912,12 +988,13 @@ export class EndpointDocGenerator {
timestamp = timestamp + 1000;
events.push(
- this.generateAlert(
- timestamp,
- entityIDSafeVersion(ancestor),
- parentEntityIDSafeVersion(ancestor),
- ancestryArray(ancestor)
- )
+ this.generateAlert({
+ ts: timestamp,
+ entityID: entityIDSafeVersion(ancestor),
+ parentEntityID: parentEntityIDSafeVersion(ancestor),
+ ancestry: ancestryArray(ancestor),
+ alertsDataStream: opts.alertsDataStream,
+ })
);
return events;
}
@@ -973,6 +1050,7 @@ export class EndpointDocGenerator {
parentEntityID: currentStateEntityID,
ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
+ eventsDataStream: opts.eventsDataStream,
});
maxChildren = this.randomN(opts.children + 1);
@@ -996,16 +1074,23 @@ export class EndpointDocGenerator {
eventType: ['end'],
ancestry,
ancestryArrayLimit: opts.ancestryArraySize,
+ eventsDataStream: opts.eventsDataStream,
});
}
if (this.randomN(100) < opts.percentWithRelated) {
- yield* this.relatedEventsGenerator(
- child,
- opts.relatedEvents,
+ yield* this.relatedEventsGenerator({
+ node: child,
+ relatedEvents: opts.relatedEvents,
processDuration,
- opts.relatedEventsOrdered
- );
- yield* this.relatedAlertsGenerator(child, opts.relatedAlerts, processDuration);
+ ordered: opts.relatedEventsOrdered,
+ eventsDataStream: opts.eventsDataStream,
+ });
+ yield* this.relatedAlertsGenerator({
+ node: child,
+ relatedAlerts: opts.relatedAlerts,
+ alertCreationTime: processDuration,
+ alertsDataStream: opts.alertsDataStream,
+ });
}
}
}
@@ -1019,12 +1104,19 @@ export class EndpointDocGenerator {
* @param ordered - if true the events will have an increasing timestamp, otherwise their timestamp will be random but
* guaranteed to be greater than or equal to the originating event
*/
- public *relatedEventsGenerator(
- node: Event,
- relatedEvents: RelatedEventInfo[] | number = 10,
- processDuration: number = 6 * 3600,
- ordered: boolean = false
- ) {
+ public *relatedEventsGenerator({
+ node,
+ relatedEvents = 10,
+ processDuration = 6 * 3600,
+ ordered = false,
+ eventsDataStream = eventsDefaultDataStream,
+ }: {
+ node: Event;
+ relatedEvents?: RelatedEventInfo[] | number;
+ processDuration?: number;
+ ordered?: boolean;
+ eventsDataStream?: DataStream;
+ }) {
let relatedEventsInfo: RelatedEventInfo[];
const nodeTimestamp = timestampSafeVersion(node) ?? 0;
let ts = nodeTimestamp + 1;
@@ -1056,6 +1148,7 @@ export class EndpointDocGenerator {
eventCategory: eventInfo.category,
eventType: eventInfo.creationType,
ancestry: ancestryArray(node),
+ eventsDataStream,
});
}
}
@@ -1067,19 +1160,26 @@ export class EndpointDocGenerator {
* @param relatedAlerts - number which defines the number of related alerts to create
* @param alertCreationTime - maximum number of seconds after process event that related alert timestamp can be
*/
- public *relatedAlertsGenerator(
- node: Event,
- relatedAlerts: number = 3,
- alertCreationTime: number = 6 * 3600
- ) {
+ public *relatedAlertsGenerator({
+ node,
+ relatedAlerts = 3,
+ alertCreationTime = 6 * 3600,
+ alertsDataStream = alertsDefaultDataStream,
+ }: {
+ node: Event;
+ relatedAlerts: number;
+ alertCreationTime: number;
+ alertsDataStream: DataStream;
+ }) {
for (let i = 0; i < relatedAlerts; i++) {
const ts = (timestampSafeVersion(node) ?? 0) + this.randomN(alertCreationTime) * 1000;
- yield this.generateAlert(
+ yield this.generateAlert({
ts,
- entityIDSafeVersion(node),
- parentEntityIDSafeVersion(node),
- ancestryArray(node)
- );
+ entityID: entityIDSafeVersion(node),
+ parentEntityID: parentEntityIDSafeVersion(node),
+ ancestry: ancestryArray(node),
+ alertsDataStream,
+ });
}
}
@@ -1227,15 +1327,21 @@ export class EndpointDocGenerator {
/**
* Generates a Host Policy response message
*/
- public generatePolicyResponse(
+ public generatePolicyResponse({
ts = new Date().getTime(),
- allStatus?: HostPolicyResponseActionStatus
- ): HostPolicyResponse {
+ allStatus,
+ policyDataStream = policyDefaultDataStream,
+ }: {
+ ts?: number;
+ allStatus?: HostPolicyResponseActionStatus;
+ policyDataStream?: DataStream;
+ } = {}): HostPolicyResponse {
const policyVersion = this.seededUUIDv4();
const status = () => {
return allStatus || this.randomHostPolicyResponseActionStatus();
};
return {
+ data_stream: policyDataStream,
'@timestamp': ts,
agent: {
id: this.commonInfo.agent.id,
diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts
index bf3d12f231c86..c0c70f9ca11af 100644
--- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts
@@ -52,10 +52,9 @@ export async function indexHostsAndAlerts(
const epmEndpointPackage = await getEndpointPackageInfo(kbnClient);
// Keep a map of host applied policy ids (fake) to real ingest package configs (policy record)
const realPolicies: Record = {};
-
for (let i = 0; i < numHosts; i++) {
const generator = new EndpointDocGenerator(random);
- await indexHostDocs(
+ await indexHostDocs({
numDocs,
client,
kbnClient,
@@ -63,10 +62,17 @@ export async function indexHostsAndAlerts(
epmEndpointPackage,
metadataIndex,
policyResponseIndex,
- fleet,
- generator
- );
- await indexAlerts(client, eventIndex, alertIndex, generator, alertsPerHost, options);
+ enrollFleet: fleet,
+ generator,
+ });
+ await indexAlerts({
+ client,
+ eventIndex,
+ alertIndex,
+ generator,
+ numAlerts: alertsPerHost,
+ options,
+ });
}
await client.indices.refresh({
index: eventIndex,
@@ -81,17 +87,27 @@ function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
-async function indexHostDocs(
- numDocs: number,
- client: Client,
- kbnClient: KbnClientWithApiKeySupport,
- realPolicies: Record,
- epmEndpointPackage: GetPackagesResponse['response'][0],
- metadataIndex: string,
- policyResponseIndex: string,
- enrollFleet: boolean,
- generator: EndpointDocGenerator
-) {
+async function indexHostDocs({
+ numDocs,
+ client,
+ kbnClient,
+ realPolicies,
+ epmEndpointPackage,
+ metadataIndex,
+ policyResponseIndex,
+ enrollFleet,
+ generator,
+}: {
+ numDocs: number;
+ client: Client;
+ kbnClient: KbnClientWithApiKeySupport;
+ realPolicies: Record;
+ epmEndpointPackage: GetPackagesResponse['response'][0];
+ metadataIndex: string;
+ policyResponseIndex: string;
+ enrollFleet: boolean;
+ generator: EndpointDocGenerator;
+}) {
const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents
const timestamp = new Date().getTime();
let hostMetadata: HostMetadata;
@@ -102,7 +118,10 @@ async function indexHostDocs(
generator.updateHostData();
generator.updateHostPolicyData();
- hostMetadata = generator.generateHostMetadata(timestamp - timeBetweenDocs * (numDocs - j - 1));
+ hostMetadata = generator.generateHostMetadata(
+ timestamp - timeBetweenDocs * (numDocs - j - 1),
+ EndpointDocGenerator.createDataStreamFromIndex(metadataIndex)
+ );
if (enrollFleet) {
const { id: appliedPolicyId, name: appliedPolicyName } = hostMetadata.Endpoint.policy.applied;
@@ -156,20 +175,30 @@ async function indexHostDocs(
});
await client.index({
index: policyResponseIndex,
- body: generator.generatePolicyResponse(timestamp - timeBetweenDocs * (numDocs - j - 1)),
+ body: generator.generatePolicyResponse({
+ ts: timestamp - timeBetweenDocs * (numDocs - j - 1),
+ policyDataStream: EndpointDocGenerator.createDataStreamFromIndex(policyResponseIndex),
+ }),
op_type: 'create',
});
}
}
-async function indexAlerts(
- client: Client,
- eventIndex: string,
- alertIndex: string,
- generator: EndpointDocGenerator,
- numAlerts: number,
- options: TreeOptions = {}
-) {
+async function indexAlerts({
+ client,
+ eventIndex,
+ alertIndex,
+ generator,
+ numAlerts,
+ options = {},
+}: {
+ client: Client;
+ eventIndex: string;
+ alertIndex: string;
+ generator: EndpointDocGenerator;
+ numAlerts: number;
+ options: TreeOptions;
+}) {
const alertGenerator = generator.alertsGenerator(numAlerts, options);
let result = alertGenerator.next();
while (!result.done) {
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
index 510f1833b793b..f2033e064ef72 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts
@@ -300,6 +300,15 @@ export interface HostResultList {
query_strategy_version: MetadataQueryStrategyVersions;
}
+/**
+ * The data_stream fields in an elasticsearch document.
+ */
+export interface DataStream {
+ dataset: string;
+ namespace: string;
+ type: string;
+}
+
/**
* Operating System metadata.
*/
@@ -556,6 +565,7 @@ export type HostMetadata = Immutable<{
version: string;
};
host: Host;
+ data_stream: DataStream;
}>;
export interface LegacyEndpointEvent {
@@ -675,6 +685,11 @@ export type SafeEndpointEvent = Partial<{
version: ECSField;
type: ECSField;
}>;
+ data_stream: Partial<{
+ type: ECSField;
+ dataset: ECSField;
+ namespace: ECSField;
+ }>;
ecs: Partial<{
version: ECSField;
}>;
@@ -1002,6 +1017,7 @@ interface HostPolicyResponseAppliedArtifact {
*/
export interface HostPolicyResponse {
'@timestamp': number;
+ data_stream: DataStream;
elastic: {
agent: {
id: string;
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
index 28889920e00e5..41665cf6d20a4 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts
@@ -168,7 +168,7 @@ describe('Custom detection rules creation', () => {
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('have.text', `${newRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${newRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', newRule.severity);
@@ -328,7 +328,7 @@ describe('Custom detection rules deletion and edition', () => {
fillAboutRule(editedRule);
saveEditedRule();
- cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', editedRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', editedRule.severity);
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
index 252ffb6c8c660..5502f35d6f0f8 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts
@@ -131,7 +131,7 @@ describe.skip('Detection rules, EQL', () => {
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('have.text', `${eqlRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${eqlRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', eqlRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', eqlRule.severity);
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
index 49ec6381cbc89..0f34e7d71e5fa 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts
@@ -115,7 +115,7 @@ describe('Detection rules, machine learning', () => {
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', machineLearningRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', machineLearningRule.severity);
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
index abc873f2df0ee..edf7305f6916e 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts
@@ -132,7 +132,7 @@ describe('Detection rules, override', () => {
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newOverrideRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', newOverrideRule.severity);
diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
index 9d988a46662fa..5095e856e3f65 100644
--- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts
@@ -129,7 +129,7 @@ describe('Detection rules, threshold', () => {
goToRuleDetails();
- cy.get(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name} Beta`);
+ cy.get(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name}`);
cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThresholdRule.description);
cy.get(ABOUT_DETAILS).within(() => {
getDetails(SEVERITY_DETAILS).should('have.text', newThresholdRule.severity);
diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
index a45b1fd18a4b6..ec3887ad72625 100644
--- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts
@@ -60,7 +60,7 @@ describe('Cases', () => {
createNewCaseWithTimeline(case1);
backToCases();
- cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases Beta');
+ cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases');
cy.get(ALL_CASES_OPEN_CASES_STATS).should('have.text', 'Open cases1');
cy.get(ALL_CASES_CLOSED_CASES_STATS).should('have.text', 'Closed cases0');
cy.get(ALL_CASES_OPEN_CASES_COUNT).should('have.text', 'Open cases (1)');
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts
index 8ce60450671b9..9f61d11b7ac0f 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts
@@ -45,8 +45,7 @@ import { openTimeline } from '../tasks/timelines';
import { OVERVIEW_URL } from '../urls/navigation';
-// FLAKY: https://github.com/elastic/kibana/issues/79389
-describe.skip('Timelines', () => {
+describe('Timelines', () => {
before(() => {
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts
index 91255d6110d59..377b2100b36cd 100644
--- a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts
+++ b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts
@@ -12,8 +12,8 @@ import {
NOTES_BUTTON,
NOTES_COUNT,
NOTES_TEXT_AREA,
+ PIN_EVENT,
TIMELINE_DESCRIPTION,
- // TIMELINE_FILTER,
TIMELINE_QUERY,
TIMELINE_TITLE,
} from '../screens/timeline';
@@ -35,7 +35,7 @@ import {
closeTimeline,
createNewTimelineTemplate,
markAsFavorite,
- openTimelineFromSettings,
+ openTimelineTemplateFromSettings,
populateTimeline,
waitForTimelineChanges,
} from '../tasks/timeline';
@@ -43,8 +43,7 @@ import { openTimeline } from '../tasks/timelines';
import { OVERVIEW_URL } from '../urls/navigation';
-// FLAKY: https://github.com/elastic/kibana/issues/79967
-describe.skip('Timeline Templates', () => {
+describe('Timeline Templates', () => {
before(() => {
cy.server();
cy.route('PATCH', '**/api/timeline').as('timeline');
@@ -56,12 +55,11 @@ describe.skip('Timeline Templates', () => {
createNewTimelineTemplate();
populateTimeline();
addFilter(timeline.filter);
- // To fix
- // cy.get(PIN_EVENT).should(
- // 'have.attr',
- // 'aria-label',
- // 'This event may not be pinned while editing a template timeline'
- // );
+ cy.get(PIN_EVENT).should(
+ 'have.attr',
+ 'aria-label',
+ 'This event may not be pinned while editing a template timeline'
+ );
cy.get(LOCKED_ICON).should('be.visible');
addNameToTimeline(timeline.title);
@@ -77,7 +75,7 @@ describe.skip('Timeline Templates', () => {
waitForTimelineChanges();
createNewTimelineTemplate();
closeTimeline();
- openTimelineFromSettings();
+ openTimelineTemplateFromSettings(timelineId);
cy.contains(timeline.title).should('exist');
cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description);
diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
index e397dd9b5a41a..98e6502ffe94f 100644
--- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts
@@ -58,6 +58,9 @@ export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]';
export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]';
+export const OPEN_TIMELINE_TEMPLATE_ICON =
+ '[data-test-subj="open-timeline-modal-body-filter-template"]';
+
export const PIN_EVENT = '[data-test-subj="pin"]';
export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]';
@@ -98,6 +101,8 @@ export const TIMELINE_FILTER = (filter: TimelineFilter) => {
export const TIMELINE_FILTER_FIELD = '[data-test-subj="filterFieldSuggestionList"]';
+export const TIMELINE_TITLE_BY_ID = (id: string) => `[data-test-subj="title-${id}"]`;
+
export const TIMELINE_FILTER_OPERATOR = '[data-test-subj="filterOperatorList"]';
export const TIMELINE_FILTER_VALUE =
diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
index 7c9c95427a4d0..b101793385488 100644
--- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
+++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts
@@ -41,9 +41,11 @@ import {
TIMELINE_INSPECT_BUTTON,
TIMELINE_SETTINGS_ICON,
TIMELINE_TITLE,
+ TIMELINE_TITLE_BY_ID,
TIMESTAMP_TOGGLE_FIELD,
TOGGLE_TIMELINE_EXPAND_EVENT,
CREATE_NEW_TIMELINE_TEMPLATE,
+ OPEN_TIMELINE_TEMPLATE_ICON,
} from '../screens/timeline';
import { TIMELINES_TABLE } from '../screens/timelines';
@@ -69,8 +71,7 @@ export const addNotesToTimeline = (notes: string) => {
export const addFilter = (filter: TimelineFilter) => {
cy.get(ADD_FILTER).click();
- cy.get(TIMELINE_FILTER_FIELD).type(filter.field);
- cy.get(COMBO_BOX).contains(filter.field).click();
+ cy.get(TIMELINE_FILTER_FIELD).type(`${filter.field}{downarrow}{enter}`);
cy.get(TIMELINE_FILTER_OPERATOR).type(filter.operator);
cy.get(COMBO_BOX).contains(filter.operator).click();
if (filter.operator !== 'exists') {
@@ -146,6 +147,12 @@ export const openTimelineFromSettings = () => {
cy.get(OPEN_TIMELINE_ICON).click({ force: true });
};
+export const openTimelineTemplateFromSettings = (id: string) => {
+ openTimelineFromSettings();
+ cy.get(OPEN_TIMELINE_TEMPLATE_ICON).click({ force: true });
+ cy.get(TIMELINE_TITLE_BY_ID(id)).click({ force: true });
+};
+
export const openTimelineSettings = () => {
cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true });
};
diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx
index 4f7b17a730b6a..5e4db16d6d9cb 100644
--- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx
+++ b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx
@@ -7,18 +7,9 @@
import React from 'react';
import { HeaderPage, HeaderPageProps } from '../../../common/components/header_page';
-import * as i18n from './translations';
const CaseHeaderPageComponent: React.FC = (props) => (
);
-CaseHeaderPageComponent.defaultProps = {
- badgeOptions: {
- beta: true,
- text: i18n.PAGE_BADGE_LABEL,
- tooltip: i18n.PAGE_BADGE_TOOLTIP,
- },
-};
-
export const CaseHeaderPage = React.memo(CaseHeaderPageComponent);
diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts b/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts
deleted file mode 100644
index 8cdc287b1584c..0000000000000
--- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const PAGE_BADGE_LABEL = i18n.translate(
- 'xpack.securitySolution.case.caseView.pageBadgeLabel',
- {
- defaultMessage: 'Beta',
- }
-);
-
-export const PAGE_BADGE_TOOLTIP = i18n.translate(
- 'xpack.securitySolution.case.caseView.pageBadgeTooltip',
- {
- defaultMessage:
- 'Case Workflow is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo.',
- }
-);
diff --git a/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts b/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts
index 54c46f064aa75..05a05ac6dd586 100644
--- a/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts
+++ b/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts
@@ -36,14 +36,14 @@ export const GET_ISSUE_API_ERROR = (id: string) =>
export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate(
'xpack.securitySolution.components.settings.jira.searchIssuesComboBoxAriaLabel',
{
- defaultMessage: 'Select parent issue',
+ defaultMessage: 'Type to search',
}
);
export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate(
'xpack.securitySolution.components.settings.jira.searchIssuesComboBoxPlaceholder',
{
- defaultMessage: 'Select parent issue',
+ defaultMessage: 'Type to search',
}
);
diff --git a/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap
index 4bd2cd05d49d0..8014431192170 100644
--- a/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/common/components/item_details_card/__snapshots__/index.test.tsx.snap
@@ -1,22 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`item_details_card ItemDetailsAction should render correctly 1`] = `
-
-
+
-
- primary
-
-
-
+ primary
+
+
`;
exports[`item_details_card ItemDetailsCard should render correctly with actions 1`] = `
@@ -24,103 +23,98 @@ exports[`item_details_card ItemDetailsCard should render correctly with actions
paddingSize="none"
>
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+ some text
+
+ some node
+
+
-
+
`;
@@ -130,66 +124,61 @@ exports[`item_details_card ItemDetailsCard should render correctly with no actio
paddingSize="none"
>