diff --git a/.buildkite/pipelines/performance/data_set_extraction_daily.yml b/.buildkite/pipelines/performance/data_set_extraction_daily.yml
index 77d410ab29c2e..39ebad2757f59 100644
--- a/.buildkite/pipelines/performance/data_set_extraction_daily.yml
+++ b/.buildkite/pipelines/performance/data_set_extraction_daily.yml
@@ -20,11 +20,11 @@ steps:
queue: n2-2-spot
depends_on: build
key: tests
- timeout_in_minutes: 60
+ timeout_in_minutes: 90
retry:
automatic:
- exit_status: '-1'
- limit: 3
+ limit: 1
- exit_status: '*'
limit: 1
diff --git a/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.test.tsx b/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.test.tsx
new file mode 100644
index 0000000000000..12b78f2afa95c
--- /dev/null
+++ b/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.test.tsx
@@ -0,0 +1,50 @@
+/*
+ * 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 { render, screen, fireEvent } from '@testing-library/react';
+import React from 'react';
+import { CancelSyncJobModal } from './sync_job_cancel_modal';
+import '@testing-library/jest-dom/extend-expect';
+import { I18nProvider } from '@kbn/i18n-react';
+
+describe('CancelSyncJobModal', () => {
+ const mockSyncJobId = '123';
+ const mockOnConfirmCb = jest.fn();
+ const mockOnCancel = jest.fn();
+
+ beforeEach(() => {
+ render(
+
+
+
+ );
+ });
+
+ test('renders the sync job ID', () => {
+ const syncJobIdElement = screen.getByTestId('confirmModalBodyText');
+ expect(syncJobIdElement).toHaveTextContent(`Sync job ID: ${mockSyncJobId}`);
+ });
+
+ test('calls onConfirmCb when confirm button is clicked', () => {
+ const confirmButton = screen.getByText('Confirm');
+ fireEvent.click(confirmButton);
+ expect(mockOnConfirmCb).toHaveBeenCalledWith(mockSyncJobId);
+ });
+
+ test('calls onCancel when cancel button is clicked', () => {
+ const cancelButton = screen.getByTestId('confirmModalCancelButton');
+ fireEvent.click(cancelButton);
+ expect(mockOnCancel).toHaveBeenCalled();
+ });
+});
diff --git a/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.tsx b/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.tsx
new file mode 100644
index 0000000000000..96f36987b3e0a
--- /dev/null
+++ b/packages/kbn-search-connectors/components/sync_jobs/sync_job_cancel_modal.tsx
@@ -0,0 +1,58 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 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 { EuiConfirmModal, EuiText, EuiCode, EuiSpacer, EuiConfirmModalProps } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+import React from 'react';
+
+export type CancelSyncModalProps = Omit & {
+ onConfirmCb: (syncJobId: string) => void;
+ syncJobId: string;
+ errorMessages?: string[];
+};
+
+export const CancelSyncJobModal: React.FC = ({
+ syncJobId,
+ onCancel,
+ onConfirmCb,
+ isLoading,
+}) => {
+ return (
+ onConfirmCb(syncJobId)}
+ cancelButtonText={i18n.translate('searchConnectors.syncJobs.cancelSyncModal.cancelButton', {
+ defaultMessage: 'Cancel',
+ })}
+ confirmButtonText={i18n.translate('searchConnectors.syncJobs.cancelSyncModal.confirmButton', {
+ defaultMessage: 'Confirm',
+ })}
+ buttonColor="danger"
+ confirmButtonDisabled={isLoading}
+ isLoading={isLoading}
+ >
+
+
+
+
+
+ {syncJobId}
+
+
+ );
+};
diff --git a/packages/kbn-search-connectors/components/sync_jobs/sync_jobs_table.tsx b/packages/kbn-search-connectors/components/sync_jobs/sync_jobs_table.tsx
index 7426d7dad3dec..4d44f6e47fa5c 100644
--- a/packages/kbn-search-connectors/components/sync_jobs/sync_jobs_table.tsx
+++ b/packages/kbn-search-connectors/components/sync_jobs/sync_jobs_table.tsx
@@ -13,16 +13,18 @@ import {
EuiBadge,
EuiBasicTable,
EuiBasicTableColumn,
+ EuiButtonIcon,
Pagination,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { ConnectorSyncJob, SyncJobType, SyncStatus } from '../..';
+import { ConnectorSyncJob, isSyncCancellable, SyncJobType, SyncStatus } from '../..';
import { syncJobTypeToText, syncStatusToColor, syncStatusToText } from '../..';
import { durationToText, getSyncJobDuration } from '../../utils/duration_to_text';
import { FormattedDateTime } from '../../utils/formatted_date_time';
import { SyncJobFlyout } from './sync_job_flyout';
+import { CancelSyncJobModal, CancelSyncModalProps } from './sync_job_cancel_modal';
interface SyncJobHistoryTableProps {
isLoading?: boolean;
@@ -30,6 +32,10 @@ interface SyncJobHistoryTableProps {
pagination: Pagination;
syncJobs: ConnectorSyncJob[];
type: 'content' | 'access_control';
+ cancelConfirmModalProps?: Pick & {
+ syncJobIdToCancel?: ConnectorSyncJob['id'];
+ setSyncJobIdToCancel: (syncJobId: ConnectorSyncJob['id'] | undefined) => void;
+ };
}
export const SyncJobsTable: React.FC = ({
@@ -38,6 +44,12 @@ export const SyncJobsTable: React.FC = ({
pagination,
syncJobs,
type,
+ cancelConfirmModalProps = {
+ onConfirmCb: () => {},
+ isLoading: false,
+ setSyncJobIdToCancel: () => {},
+ syncJobIdToCancel: undefined,
+ },
}) => {
const [selectedSyncJob, setSelectedSyncJob] = useState(undefined);
const columns: Array> = [
@@ -127,6 +139,33 @@ export const SyncJobsTable: React.FC = ({
onClick: (job) => setSelectedSyncJob(job),
type: 'icon',
},
+ ...(cancelConfirmModalProps
+ ? [
+ {
+ render: (job: ConnectorSyncJob) => {
+ return isSyncCancellable(job.status) ? (
+ cancelConfirmModalProps.setSyncJobIdToCancel(job.id)}
+ aria-label={i18n.translate(
+ 'searchConnectors.index.syncJobs.actions.cancelSyncJob.caption',
+ {
+ defaultMessage: 'Cancel this sync job',
+ }
+ )}
+ >
+ {i18n.translate('searchConnectors.index.syncJobs.actions.deleteJob.caption', {
+ defaultMessage: 'Delete',
+ })}
+
+ ) : (
+ <>>
+ );
+ },
+ },
+ ]
+ : []),
],
},
];
@@ -136,6 +175,13 @@ export const SyncJobsTable: React.FC = ({
{Boolean(selectedSyncJob) && (
setSelectedSyncJob(undefined)} syncJob={selectedSyncJob} />
)}
+ {Boolean(cancelConfirmModalProps) && cancelConfirmModalProps?.syncJobIdToCancel && (
+ cancelConfirmModalProps.setSyncJobIdToCancel(undefined)}
+ />
+ )}
{
+ const mockClient = {
+ transport: {
+ request: jest.fn(),
+ },
+ };
+
+ it('should cancel a sync', async () => {
+ mockClient.transport.request.mockImplementation(() => ({
+ success: true,
+ }));
+
+ await expect(cancelSync(mockClient as unknown as ElasticsearchClient, '1234')).resolves.toEqual(
+ { success: true }
+ );
+ expect(mockClient.transport.request).toHaveBeenCalledWith({
+ method: 'PUT',
+ path: '/_connector/_sync_job/1234/_cancel',
+ });
+ });
+});
diff --git a/packages/kbn-search-connectors/lib/cancel_sync.ts b/packages/kbn-search-connectors/lib/cancel_sync.ts
new file mode 100644
index 0000000000000..b3f31bb8a41b4
--- /dev/null
+++ b/packages/kbn-search-connectors/lib/cancel_sync.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 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 { ElasticsearchClient } from '@kbn/core-elasticsearch-server';
+import { ConnectorAPICancelSyncResponse } from '../types';
+
+export const cancelSync = async (client: ElasticsearchClient, syncJobId: string) => {
+ const result = await client.transport.request({
+ method: 'PUT',
+ path: `/_connector/_sync_job/${syncJobId}/_cancel`,
+ });
+ return result;
+};
diff --git a/packages/kbn-search-connectors/lib/collect_connector_stats.ts b/packages/kbn-search-connectors/lib/collect_connector_stats.ts
index c2ad0fe17f3dd..80edd3b0ca3a9 100644
--- a/packages/kbn-search-connectors/lib/collect_connector_stats.ts
+++ b/packages/kbn-search-connectors/lib/collect_connector_stats.ts
@@ -362,6 +362,8 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
let idle = 0;
let running = 0;
let duration = 0;
+ const errors = new Map();
+ let topErrors: string[] = [];
for (const syncJob of syncJobs) {
completed += syncJob.status === SyncStatus.COMPLETED ? 1 : 0;
@@ -386,6 +388,18 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
duration += Math.floor((completedAt.getTime() - startedAt.getTime()) / 1000);
}
}
+ if (syncJob.status === SyncStatus.ERROR && syncJob.error) {
+ errors.set(syncJob.error, (errors.get(syncJob.error) ?? 0) + 1);
+ }
+ }
+
+ if (errors.size <= 5) {
+ topErrors = [...errors.keys()];
+ } else {
+ topErrors = [...errors.entries()]
+ .sort((a, b) => b[1] - a[1])
+ .map((a) => a[0])
+ .slice(0, 5);
}
return {
@@ -399,5 +413,6 @@ function syncJobsStatsByState(syncJobs: ConnectorSyncJob[]): SyncJobStatsByState
idle,
running,
totalDurationSeconds: duration,
+ topErrors,
} as SyncJobStatsByState;
}
diff --git a/packages/kbn-search-connectors/lib/collect_connector_stats_test_data.ts b/packages/kbn-search-connectors/lib/collect_connector_stats_test_data.ts
index 73614cfb66787..d510fe5ad5e66 100644
--- a/packages/kbn-search-connectors/lib/collect_connector_stats_test_data.ts
+++ b/packages/kbn-search-connectors/lib/collect_connector_stats_test_data.ts
@@ -183,6 +183,7 @@ export const spoIncrementalSyncJob: ConnectorSyncJob = {
job_type: SyncJobType.INCREMENTAL,
status: SyncStatus.ERROR,
trigger_method: TriggerMethod.ON_DEMAND,
+ error: 'spo_incremental_error',
connector: {
id: spoConnector.id,
configuration: {
@@ -315,6 +316,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 220,
+ topErrors: ['spo_incremental_error'],
},
accessControl: {
total: 1,
@@ -327,6 +329,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 20,
+ topErrors: [],
},
full: {
total: 1,
@@ -339,6 +342,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: [],
},
incremental: {
total: 1,
@@ -351,6 +355,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: ['spo_incremental_error'],
},
},
last7Days: {
@@ -365,6 +370,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 120,
+ topErrors: ['spo_incremental_error'],
},
accessControl: {
total: 1,
@@ -377,6 +383,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 20,
+ topErrors: [],
},
incremental: {
total: 1,
@@ -389,6 +396,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: ['spo_incremental_error'],
},
},
},
@@ -406,6 +414,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 220,
+ topErrors: ['spo_incremental_error'],
},
accessControl: {
total: 1,
@@ -418,6 +427,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 20,
+ topErrors: [],
},
full: {
total: 1,
@@ -430,6 +440,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: [],
},
incremental: {
total: 1,
@@ -442,6 +453,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: ['spo_incremental_error'],
},
},
last7Days: {
@@ -456,6 +468,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 120,
+ topErrors: ['spo_incremental_error'],
},
accessControl: {
total: 1,
@@ -468,6 +481,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 20,
+ topErrors: [],
},
incremental: {
total: 1,
@@ -480,6 +494,7 @@ export const expectedSpoConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 100,
+ topErrors: ['spo_incremental_error'],
},
},
},
@@ -543,6 +558,7 @@ export const expectedMysqlConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 200,
+ topErrors: [],
},
full: {
total: 1,
@@ -555,6 +571,7 @@ export const expectedMysqlConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 200,
+ topErrors: [],
},
},
},
@@ -579,6 +596,7 @@ export const expectedDeletedConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 200,
+ topErrors: [],
},
full: {
total: 1,
@@ -591,6 +609,7 @@ export const expectedDeletedConnectorStats: ConnectorStats = {
idle: 0,
running: 0,
totalDurationSeconds: 200,
+ topErrors: [],
},
},
},
diff --git a/packages/kbn-search-connectors/lib/index.ts b/packages/kbn-search-connectors/lib/index.ts
index 80bd6c554c54c..ed2f10a7f9ea3 100644
--- a/packages/kbn-search-connectors/lib/index.ts
+++ b/packages/kbn-search-connectors/lib/index.ts
@@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
+export * from './cancel_sync';
export * from './cancel_syncs';
export * from './collect_connector_stats';
export * from './create_connector';
diff --git a/packages/kbn-search-connectors/tsconfig.json b/packages/kbn-search-connectors/tsconfig.json
index 732b92333947f..eb7decb3d1e00 100644
--- a/packages/kbn-search-connectors/tsconfig.json
+++ b/packages/kbn-search-connectors/tsconfig.json
@@ -5,7 +5,9 @@
"types": [
"jest",
"node",
- "react"
+ "react",
+ "@testing-library/jest-dom",
+ "@testing-library/react",
]
},
"include": [
diff --git a/packages/kbn-search-connectors/types/connector_stats.ts b/packages/kbn-search-connectors/types/connector_stats.ts
index 7c72a4b669b6e..93401fd590801 100644
--- a/packages/kbn-search-connectors/types/connector_stats.ts
+++ b/packages/kbn-search-connectors/types/connector_stats.ts
@@ -121,4 +121,5 @@ export interface SyncJobStatsByState {
idle: number;
running: number;
totalDurationSeconds: number;
+ topErrors: string[];
}
diff --git a/packages/kbn-search-connectors/types/connectors_api.ts b/packages/kbn-search-connectors/types/connectors_api.ts
index 265aee65cc68c..8869847e34da5 100644
--- a/packages/kbn-search-connectors/types/connectors_api.ts
+++ b/packages/kbn-search-connectors/types/connectors_api.ts
@@ -22,3 +22,7 @@ export interface ConnectorsAPISyncJobResponse {
export interface ConnectorSecretCreateResponse {
id: string;
}
+
+export interface ConnectorAPICancelSyncResponse {
+ success: boolean;
+}
diff --git a/packages/kbn-search-connectors/utils/identify_exceptions.ts b/packages/kbn-search-connectors/utils/identify_exceptions.ts
index 0bc395710d8f5..ae825df6bced0 100644
--- a/packages/kbn-search-connectors/utils/identify_exceptions.ts
+++ b/packages/kbn-search-connectors/utils/identify_exceptions.ts
@@ -11,6 +11,10 @@ export interface ElasticsearchResponseError {
body?: {
error?: {
type: string;
+ caused_by?: {
+ type?: string;
+ reason?: string;
+ };
};
};
statusCode?: number;
@@ -48,3 +52,12 @@ export const isMissingAliasException = (error: ElasticsearchResponseError) =>
error.meta?.statusCode === 404 &&
typeof error.meta?.body?.error === 'string' &&
MISSING_ALIAS_ERROR.test(error.meta?.body?.error);
+
+export const isStatusTransitionException = (error: ElasticsearchResponseError) => {
+ return (
+ error.meta?.statusCode === 400 &&
+ error.meta?.body?.error?.type === 'status_exception' &&
+ error.meta?.body.error?.caused_by?.type ===
+ 'connector_sync_job_invalid_status_transition_exception'
+ );
+};
diff --git a/packages/kbn-search-connectors/utils/sync_status_to_text.test.ts b/packages/kbn-search-connectors/utils/sync_status_to_text.test.ts
index 6c448ea241dd2..752c21e2672ec 100644
--- a/packages/kbn-search-connectors/utils/sync_status_to_text.test.ts
+++ b/packages/kbn-search-connectors/utils/sync_status_to_text.test.ts
@@ -6,9 +6,8 @@
* Side Public License, v 1.
*/
-import { SyncStatus } from '..';
-
import { syncStatusToColor, syncStatusToText } from './sync_status_to_text';
+import { isSyncCancellable, SyncStatus } from '..';
describe('syncStatusToText', () => {
it('should return correct value for completed', () => {
@@ -57,3 +56,33 @@ describe('syncStatusToColor', () => {
expect(syncStatusToColor(SyncStatus.SUSPENDED)).toEqual('warning');
});
});
+
+describe('isSyncCancellable', () => {
+ it('should return true for in progress status', () => {
+ expect(isSyncCancellable(SyncStatus.IN_PROGRESS)).toBe(true);
+ });
+
+ it('should return true for pending status', () => {
+ expect(isSyncCancellable(SyncStatus.PENDING)).toBe(true);
+ });
+
+ it('should return true for suspended status', () => {
+ expect(isSyncCancellable(SyncStatus.SUSPENDED)).toBe(true);
+ });
+
+ it('should return false for canceling status', () => {
+ expect(isSyncCancellable(SyncStatus.CANCELING)).toBe(false);
+ });
+
+ it('should return false for completed status', () => {
+ expect(isSyncCancellable(SyncStatus.COMPLETED)).toBe(false);
+ });
+
+ it('should return false for error status', () => {
+ expect(isSyncCancellable(SyncStatus.ERROR)).toBe(false);
+ });
+
+ it('should return false for canceled status', () => {
+ expect(isSyncCancellable(SyncStatus.CANCELED)).toBe(false);
+ });
+});
diff --git a/packages/kbn-search-connectors/utils/sync_status_to_text.ts b/packages/kbn-search-connectors/utils/sync_status_to_text.ts
index b00e873bd52e0..11491a02be43f 100644
--- a/packages/kbn-search-connectors/utils/sync_status_to_text.ts
+++ b/packages/kbn-search-connectors/utils/sync_status_to_text.ts
@@ -62,6 +62,14 @@ export function syncStatusToColor(status: SyncStatus): string {
}
}
+export const isSyncCancellable = (syncStatus: SyncStatus): boolean => {
+ return (
+ syncStatus === SyncStatus.IN_PROGRESS ||
+ syncStatus === SyncStatus.PENDING ||
+ syncStatus === SyncStatus.SUSPENDED
+ );
+};
+
export const syncJobTypeToText = (syncType: SyncJobType): string => {
switch (syncType) {
case SyncJobType.FULL:
diff --git a/src/plugins/data/common/query/text_based_query_state_to_ast.ts b/src/plugins/data/common/query/text_based_query_state_to_ast.ts
index e24cbbd0a7dab..fe60157800501 100644
--- a/src/plugins/data/common/query/text_based_query_state_to_ast.ts
+++ b/src/plugins/data/common/query/text_based_query_state_to_ast.ts
@@ -20,13 +20,19 @@ import {
interface Args extends QueryState {
timeFieldName?: string;
inputQuery?: Query;
+ titleForInspector?: string;
+ descriptionForInspector?: string;
}
/**
* Converts QueryState to expression AST
* @param filters array of kibana filters
* @param query kibana query or aggregate query
+ * @param inputQuery
* @param time kibana time range
+ * @param dataView
+ * @param titleForInspector
+ * @param descriptionForInspector
*/
export function textBasedQueryStateToExpressionAst({
filters,
@@ -34,6 +40,8 @@ export function textBasedQueryStateToExpressionAst({
inputQuery,
time,
timeFieldName,
+ titleForInspector,
+ descriptionForInspector,
}: Args) {
const kibana = buildExpressionFunction('kibana', {});
let q;
@@ -51,7 +59,12 @@ export function textBasedQueryStateToExpressionAst({
const mode = getAggregateQueryMode(query);
for (const esMode of ['sql', 'esql']) {
if (mode === esMode && esMode in query) {
- const essql = aggregateQueryToAst(query, timeFieldName);
+ const essql = aggregateQueryToAst({
+ query,
+ timeField: timeFieldName,
+ titleForInspector,
+ descriptionForInspector,
+ });
if (essql) {
ast.chain.push(essql);
diff --git a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts
index e9ded5d83b8c0..9e99bcc150053 100644
--- a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts
+++ b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.test.ts
@@ -72,4 +72,35 @@ describe('textBasedQueryStateToAstWithValidation', () => {
})
);
});
+
+ it('returns an object with the correct structure for ES|QL', async () => {
+ const dataView = createStubDataView({
+ spec: {
+ id: 'foo',
+ title: 'foo',
+ timeFieldName: '@timestamp',
+ },
+ });
+ const actual = await textBasedQueryStateToAstWithValidation({
+ filters: [],
+ query: { esql: 'from logs*' },
+ time: {
+ from: 'now',
+ to: 'now+7d',
+ },
+ dataView,
+ titleForInspector: 'Custom title',
+ descriptionForInspector: 'Custom desc',
+ });
+ expect(actual).toHaveProperty(
+ 'chain.2.arguments',
+ expect.objectContaining({
+ query: ['from logs*'],
+ timeField: ['@timestamp'],
+ locale: ['en'],
+ titleForInspector: ['Custom title'],
+ descriptionForInspector: ['Custom desc'],
+ })
+ );
+ });
});
diff --git a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts
index 4d38aad530f45..27e6484a2d430 100644
--- a/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts
+++ b/src/plugins/data/common/query/text_based_query_state_to_ast_with_validation.ts
@@ -14,13 +14,19 @@ interface Args extends QueryState {
dataView?: DataView;
inputQuery?: Query;
timeFieldName?: string;
+ titleForInspector?: string;
+ descriptionForInspector?: string;
}
/**
* Converts QueryState to expression AST
* @param filters array of kibana filters
* @param query kibana query or aggregate query
+ * @param inputQuery
* @param time kibana time range
+ * @param dataView
+ * @param titleForInspector
+ * @param descriptionForInspector
*/
export async function textBasedQueryStateToAstWithValidation({
filters,
@@ -28,6 +34,8 @@ export async function textBasedQueryStateToAstWithValidation({
inputQuery,
time,
dataView,
+ titleForInspector,
+ descriptionForInspector,
}: Args) {
let ast;
if (query && isOfAggregateQueryType(query)) {
@@ -37,6 +45,8 @@ export async function textBasedQueryStateToAstWithValidation({
inputQuery,
time,
timeFieldName: dataView?.timeFieldName,
+ titleForInspector,
+ descriptionForInspector,
});
}
return ast;
diff --git a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts
index 0ded432eb0508..23a20fbde3f4b 100644
--- a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts
+++ b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.test.ts
@@ -10,15 +10,37 @@ import { aggregateQueryToAst } from './aggregate_query_to_ast';
describe('aggregateQueryToAst', () => {
it('should return a function', () => {
- expect(aggregateQueryToAst({ esql: 'from foo' })).toHaveProperty('type', 'function');
+ expect(aggregateQueryToAst({ query: { esql: 'from foo' } })).toHaveProperty('type', 'function');
});
it('should forward arguments', () => {
- expect(aggregateQueryToAst({ esql: 'from foo' }, 'baz')).toHaveProperty(
+ expect(
+ aggregateQueryToAst({
+ query: { esql: 'from foo' },
+ timeField: 'baz',
+ })
+ ).toHaveProperty(
+ 'arguments',
+ expect.objectContaining({
+ query: ['from foo'],
+ timeField: ['baz'],
+ })
+ );
+
+ expect(
+ aggregateQueryToAst({
+ query: { esql: 'from foo' },
+ timeField: 'baz',
+ titleForInspector: 'Custom title',
+ descriptionForInspector: 'Custom desc',
+ })
+ ).toHaveProperty(
'arguments',
expect.objectContaining({
query: ['from foo'],
timeField: ['baz'],
+ titleForInspector: ['Custom title'],
+ descriptionForInspector: ['Custom desc'],
})
);
});
diff --git a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts
index 6b69af873585b..e755f6a9f6dbe 100644
--- a/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts
+++ b/src/plugins/data/common/search/expressions/aggregate_query_to_ast.ts
@@ -11,10 +11,17 @@ import { AggregateQuery } from '../../query';
import { EssqlExpressionFunctionDefinition } from './essql';
import { EsqlExpressionFunctionDefinition } from './esql';
-export const aggregateQueryToAst = (
- query: AggregateQuery,
- timeField?: string
-): undefined | ExpressionAstFunction => {
+export const aggregateQueryToAst = ({
+ query,
+ timeField,
+ titleForInspector,
+ descriptionForInspector,
+}: {
+ query: AggregateQuery;
+ timeField?: string;
+ titleForInspector?: string;
+ descriptionForInspector?: string;
+}): undefined | ExpressionAstFunction => {
if ('sql' in query) {
return buildExpressionFunction('essql', {
query: query.sql,
@@ -26,6 +33,8 @@ export const aggregateQueryToAst = (
query: query.esql,
timeField,
locale: i18n.getLocale(),
+ titleForInspector,
+ descriptionForInspector,
}).toAst();
}
};
diff --git a/src/plugins/data/common/search/expressions/esql.ts b/src/plugins/data/common/search/expressions/esql.ts
index d9893c8c203f6..a40902b6658ce 100644
--- a/src/plugins/data/common/search/expressions/esql.ts
+++ b/src/plugins/data/common/search/expressions/esql.ts
@@ -40,6 +40,12 @@ interface Arguments {
// timezone?: string;
timeField?: string;
locale?: string;
+
+ /**
+ * Requests' meta for showing in Inspector
+ */
+ titleForInspector?: string;
+ descriptionForInspector?: string;
}
export type EsqlExpressionFunctionDefinition = ExpressionFunctionDefinition<
@@ -107,10 +113,24 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
defaultMessage: 'The locale to use.',
}),
},
+ titleForInspector: {
+ aliases: ['titleForInspector'],
+ types: ['string'],
+ help: i18n.translate('data.search.esql.titleForInspector.help', {
+ defaultMessage: 'The title to show in Inspector.',
+ }),
+ },
+ descriptionForInspector: {
+ aliases: ['descriptionForInspector'],
+ types: ['string'],
+ help: i18n.translate('data.search.esql.descriptionForInspector.help', {
+ defaultMessage: 'The description to show in Inspector.',
+ }),
+ },
},
fn(
input,
- { query, /* timezone, */ timeField, locale },
+ { query, /* timezone, */ timeField, locale, titleForInspector, descriptionForInspector },
{ abortSignal, inspectorAdapters, getKibanaRequest }
) {
return defer(() =>
@@ -158,14 +178,17 @@ export const getEsqlFn = ({ getStartDependencies }: EsqlFnArguments) => {
}
const request = inspectorAdapters.requests.start(
- i18n.translate('data.search.dataRequest.title', {
- defaultMessage: 'Data',
- }),
- {
- description: i18n.translate('data.search.es_search.dataRequest.description', {
- defaultMessage:
- 'This request queries Elasticsearch to fetch the data for the visualization.',
+ titleForInspector ??
+ i18n.translate('data.search.dataRequest.title', {
+ defaultMessage: 'Data',
}),
+ {
+ description:
+ descriptionForInspector ??
+ i18n.translate('data.search.es_search.dataRequest.description', {
+ defaultMessage:
+ 'This request queries Elasticsearch to fetch the data for the visualization.',
+ }),
},
startTime
);
diff --git a/src/plugins/discover/public/application/main/utils/fetch_text_based.ts b/src/plugins/discover/public/application/main/utils/fetch_text_based.ts
index 4099a16a9625a..7045eb3c28cbc 100644
--- a/src/plugins/discover/public/application/main/utils/fetch_text_based.ts
+++ b/src/plugins/discover/public/application/main/utils/fetch_text_based.ts
@@ -7,6 +7,7 @@
*/
import { pluck } from 'rxjs';
import { lastValueFrom } from 'rxjs';
+import { i18n } from '@kbn/i18n';
import { Query, AggregateQuery, Filter } from '@kbn/es-query';
import type { Adapters } from '@kbn/inspector-plugin/common';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
@@ -41,6 +42,12 @@ export function fetchTextBased(
time: timeRange,
dataView,
inputQuery,
+ titleForInspector: i18n.translate('discover.inspectorTextBasedRequestTitle', {
+ defaultMessage: 'Table',
+ }),
+ descriptionForInspector: i18n.translate('discover.inspectorTextBasedRequestDescription', {
+ defaultMessage: 'This request queries Elasticsearch to fetch results for the table.',
+ }),
})
.then((ast) => {
if (ast) {
diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
index 8a40bd598e1c6..35f0644b08c88 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts
@@ -484,6 +484,10 @@ export const stackManagementSchema: MakeSchemaFrom = {
type: 'keyword',
_meta: { description: 'Non-default value of setting.' },
},
+ 'observability:aiAssistantSimulatedFunctionCalling': {
+ type: 'boolean',
+ _meta: { description: 'Non-default value of setting.' },
+ },
'observability:logsExplorer:allowedDataViews': {
type: 'array',
items: {
diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
index e25ebd87afa9a..75c2ca310ddc6 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts
@@ -54,6 +54,7 @@ export interface UsageStats {
'observability:logsExplorer:allowedDataViews': string[];
'observability:aiAssistantLogsIndexPattern': string;
'observability:aiAssistantResponseLanguage': string;
+ 'observability:aiAssistantSimulatedFunctionCalling': boolean;
'visualization:heatmap:maxBuckets': number;
'visualization:colorMapping': string;
'visualization:useLegacyTimeAxis': boolean;
diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json
index 8c6c605bd6c72..1a0b63d5c6bf6 100644
--- a/src/plugins/telemetry/schema/oss_plugins.json
+++ b/src/plugins/telemetry/schema/oss_plugins.json
@@ -10182,6 +10182,12 @@
"description": "Non-default value of setting."
}
},
+ "observability:aiAssistantSimulatedFunctionCalling": {
+ "type": "boolean",
+ "_meta": {
+ "description": "Non-default value of setting."
+ }
+ },
"banners:placement": {
"type": "keyword",
"_meta": {
diff --git a/test/functional/apps/discover/group2/_data_grid_field_tokens.ts b/test/functional/apps/discover/group2/_data_grid_field_tokens.ts
index 0782180ab79f0..74586018761aa 100644
--- a/test/functional/apps/discover/group2/_data_grid_field_tokens.ts
+++ b/test/functional/apps/discover/group2/_data_grid_field_tokens.ts
@@ -32,12 +32,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
};
async function findFirstColumnTokens() {
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.discover.waitUntilSearchingHasFinished();
return await findFirstFieldIcons('euiDataGridBody > dataGridHeader');
}
async function findFirstDocViewerTokens() {
- await dataGrid.clickRowToggle({ rowIndex: 0 });
- return await findFirstFieldIcons('docTableDetailsFlyout');
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.discover.waitUntilSearchingHasFinished();
+ let fieldTokens: string[] | undefined = [];
+ await retry.try(async () => {
+ await dataGrid.clickRowToggle({ rowIndex: 0 });
+ fieldTokens = await findFirstFieldIcons('docTableDetailsFlyout');
+ });
+ return fieldTokens;
}
async function findFirstFieldIcons(elementSelector: string) {
@@ -62,8 +70,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
return firstFieldIcons;
}
- // FLAKY: https://github.com/elastic/kibana/issues/180622
- describe.skip('discover data grid field tokens', function () {
+ describe('discover data grid field tokens', function () {
before(async () => {
await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']);
await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional');
diff --git a/test/functional/apps/discover/group4/_esql_view.ts b/test/functional/apps/discover/group4/_esql_view.ts
index 9147470d8282a..31f323500b119 100644
--- a/test/functional/apps/discover/group4/_esql_view.ts
+++ b/test/functional/apps/discover/group4/_esql_view.ts
@@ -18,6 +18,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const monacoEditor = getService('monacoEditor');
const security = getService('security');
+ const inspector = getService('inspector');
const retry = getService('retry');
const browser = getService('browser');
const find = getService('find');
@@ -256,6 +257,23 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
});
+ describe('inspector', () => {
+ beforeEach(async () => {
+ await PageObjects.common.navigateToApp('discover');
+ await PageObjects.timePicker.setDefaultAbsoluteRange();
+ });
+
+ it('shows Discover and Lens requests in Inspector', async () => {
+ await PageObjects.discover.selectTextBaseLang();
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.discover.waitUntilSearchingHasFinished();
+ await inspector.open();
+ const requestNames = await inspector.getRequestNames();
+ expect(requestNames).to.contain('Table');
+ expect(requestNames).to.contain('Visualization');
+ });
+ });
+
describe('query history', () => {
beforeEach(async () => {
await PageObjects.common.navigateToApp('discover');
diff --git a/x-pack/plugins/enterprise_search/common/types/connector_stats.ts b/x-pack/plugins/enterprise_search/common/types/connector_stats.ts
index b48d75120634d..39fe79bf8dad6 100644
--- a/x-pack/plugins/enterprise_search/common/types/connector_stats.ts
+++ b/x-pack/plugins/enterprise_search/common/types/connector_stats.ts
@@ -118,4 +118,5 @@ export interface SyncJobStatsByState {
idle: number;
running: number;
totalDurationSeconds: number;
+ topErrors: string[];
}
diff --git a/x-pack/plugins/enterprise_search/common/types/error_codes.ts b/x-pack/plugins/enterprise_search/common/types/error_codes.ts
index 5370cbab778b8..1fe2d557d15c9 100644
--- a/x-pack/plugins/enterprise_search/common/types/error_codes.ts
+++ b/x-pack/plugins/enterprise_search/common/types/error_codes.ts
@@ -25,6 +25,7 @@ export enum ErrorCode {
SEARCH_APPLICATION_ALREADY_EXISTS = 'search_application_already_exists',
SEARCH_APPLICATION_NAME_INVALID = 'search_application_name_invalid',
SEARCH_APPLICATION_NOT_FOUND = 'search_application_not_found',
+ STATUS_TRANSITION_ERROR = 'status_transition_error',
UNAUTHORIZED = 'unauthorized',
UNCAUGHT_EXCEPTION = 'uncaught_exception',
}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.test.ts
new file mode 100644
index 0000000000000..8b0e9ff6b17ae
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+// write tests that checks cancelSync API logic calls correct endpoint
+import { mockHttpValues } from '../../../__mocks__/kea_logic';
+
+import { nextTick } from '@kbn/test-jest-helpers';
+
+import { cancelSync } from './cancel_sync_api_logic';
+
+describe('CancelSyncApiLogic', () => {
+ const { http } = mockHttpValues;
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ describe('cancelSync', () => {
+ it('calls correct api', async () => {
+ const promise = Promise.resolve({ success: true });
+ http.put.mockReturnValue(promise);
+ const result = cancelSync({ syncJobId: 'syncJobId1' });
+ await nextTick();
+ expect(http.put).toHaveBeenCalledWith(
+ '/internal/enterprise_search/connectors/syncJobId1/cancel_sync'
+ );
+ await expect(result).resolves.toEqual({ success: true });
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.ts
new file mode 100644
index 0000000000000..21873bdf8958c
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/api/connector/cancel_sync_api_logic.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { Actions, createApiLogic } from '../../../shared/api_logic/create_api_logic';
+import { HttpLogic } from '../../../shared/http';
+
+export interface CancelSyncApiArgs {
+ syncJobId: string;
+}
+
+export interface CancelSyncApiResponse {
+ success: boolean;
+}
+
+export const cancelSync = async ({ syncJobId }: CancelSyncApiArgs) => {
+ const route = `/internal/enterprise_search/connectors/${syncJobId}/cancel_sync`;
+ return await HttpLogic.values.http.put(route);
+};
+
+export const CancelSyncApiLogic = createApiLogic(['cancel_sync_api_logic'], cancelSync, {
+ showErrorFlash: true,
+ showSuccessFlashFn: () =>
+ i18n.translate('xpack.enterpriseSearch.content.searchIndex.cancelSync.successMessage', {
+ defaultMessage: 'Successfully canceled sync',
+ }),
+});
+
+export type CancelSyncApiActions = Actions;
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 123a19cefff37..f8a9b8f12bcb0 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
@@ -35,8 +35,7 @@ import { ConnectorViewLogic } from './connector_view_logic';
export const ConnectorDetailOverview: React.FC = () => {
const { indexData } = useValues(IndexViewLogic);
- const { connector } = useValues(ConnectorViewLogic);
- const error = null;
+ const { connector, error } = useValues(ConnectorViewLogic);
const { isCloud } = useValues(KibanaLogic);
const { showModal } = useActions(ConvertConnectorLogic);
const { isModalVisible } = useValues(ConvertConnectorLogic);
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/cancel_syncs_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/cancel_syncs_logic.ts
index 43ae9d81a7336..055ace3cbc013 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/cancel_syncs_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/cancel_syncs_logic.ts
@@ -22,7 +22,7 @@ interface CancelSyncsLogicValues {
isConnectorIndex: boolean;
}
-interface CancelSyncsLogicActions {
+export interface CancelSyncsLogicActions {
cancelSyncs: () => void;
cancelSyncsApiError: CancelSyncsApiActions['apiError'];
cancelSyncsApiSuccess: CancelSyncsApiActions['apiSuccess'];
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 0e0b5405eda09..57f6d8f11bfce 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
@@ -240,6 +240,8 @@ export const IndexViewLogic = kea false,
+ startAccessControlSync: () => true,
+ startIncrementalSync: () => true,
startSyncApiSuccess: () => true,
},
],
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 b21b312730bc2..2013d601df4aa 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
@@ -5,9 +5,8 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React, { useEffect } from 'react';
-import { type } from 'io-ts';
import { useActions, useValues } from 'kea';
import { EuiButtonGroup } from '@elastic/eui';
@@ -25,11 +24,19 @@ import { SyncJobsViewLogic } from './sync_jobs_view_logic';
export const SyncJobs: React.FC = () => {
const { hasDocumentLevelSecurityFeature } = useValues(IndexViewLogic);
const { productFeatures } = useValues(KibanaLogic);
- const [selectedSyncJobCategory, setSelectedSyncJobCategory] = useState('content');
const shouldShowAccessSyncs =
productFeatures.hasDocumentLevelSecurityEnabled && hasDocumentLevelSecurityFeature;
- const { connectorId, syncJobsPagination: pagination, syncJobs } = useValues(SyncJobsViewLogic);
- const { fetchSyncJobs } = useActions(SyncJobsViewLogic);
+ const {
+ connectorId,
+ syncJobsPagination: pagination,
+ syncJobs,
+ cancelSyncJobLoading,
+ syncJobToCancel,
+ selectedSyncJobCategory,
+ syncTriggeredLocally,
+ } = useValues(SyncJobsViewLogic);
+ const { fetchSyncJobs, cancelSyncJob, setCancelSyncJob, setSelectedSyncJobCategory } =
+ useActions(SyncJobsViewLogic);
useEffect(() => {
if (connectorId) {
@@ -37,10 +44,10 @@ export const SyncJobs: React.FC = () => {
connectorId,
from: pagination.pageIndex * (pagination.pageSize || 0),
size: pagination.pageSize ?? 10,
- type: selectedSyncJobCategory as 'access_control' | 'content',
+ type: selectedSyncJobCategory,
});
}
- }, [connectorId, selectedSyncJobCategory, type]);
+ }, [connectorId, selectedSyncJobCategory]);
return (
<>
@@ -56,7 +63,9 @@ export const SyncJobs: React.FC = () => {
)}
idSelected={selectedSyncJobCategory}
onChange={(optionId) => {
- setSelectedSyncJobCategory(optionId);
+ if (optionId === 'content' || optionId === 'access_control') {
+ setSelectedSyncJobCategory(optionId);
+ }
}}
options={[
{
@@ -79,6 +88,7 @@ export const SyncJobs: React.FC = () => {
)}
{selectedSyncJobCategory === 'content' ? (
{
if (connectorId) {
fetchSyncJobs({
@@ -92,9 +102,18 @@ export const SyncJobs: React.FC = () => {
pagination={pagination}
syncJobs={syncJobs}
type="content"
+ cancelConfirmModalProps={{
+ isLoading: cancelSyncJobLoading,
+ onConfirmCb: (syncJobId: string) => {
+ cancelSyncJob({ syncJobId });
+ },
+ setSyncJobIdToCancel: setCancelSyncJob,
+ syncJobIdToCancel: syncJobToCancel ?? undefined,
+ }}
/>
) : (
{
if (connectorId) {
fetchSyncJobs({
@@ -105,6 +124,14 @@ export const SyncJobs: React.FC = () => {
});
}
}}
+ cancelConfirmModalProps={{
+ isLoading: cancelSyncJobLoading,
+ onConfirmCb: (syncJobId: string) => {
+ cancelSyncJob({ syncJobId });
+ },
+ setSyncJobIdToCancel: setCancelSyncJob,
+ syncJobIdToCancel: syncJobToCancel ?? undefined,
+ }}
pagination={pagination}
syncJobs={syncJobs}
type="access_control"
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts
index d1c3b81e73b3d..08275a7e2c49f 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.test.ts
@@ -23,7 +23,11 @@ import { SyncJobView, SyncJobsViewLogic } from './sync_jobs_view_logic';
// We can't test fetchTimeOutId because this will get set whenever the logic is created
// And the timeoutId is non-deterministic. We use expect.object.containing throughout this test file
const DEFAULT_VALUES = {
+ cancelSyncJobLoading: false,
+ cancelSyncJobStatus: Status.IDLE,
connectorId: null,
+ selectedSyncJobCategory: 'content',
+ syncJobToCancel: null,
syncJobs: [],
syncJobsData: undefined,
syncJobsLoading: true,
@@ -33,6 +37,7 @@ const DEFAULT_VALUES = {
totalItemCount: 0,
},
syncJobsStatus: Status.IDLE,
+ syncTriggeredLocally: false,
};
describe('SyncJobsViewLogic', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts
index b820cfebaf8f9..e0a6176e38bff 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/sync_jobs/sync_jobs_view_logic.ts
@@ -7,6 +7,7 @@
import { kea, MakeLogicType } from 'kea';
+import { isEqual } from 'lodash';
import moment from 'moment';
import { Pagination } from '@elastic/eui';
@@ -16,35 +17,68 @@ import { Status } from '../../../../../../common/types/api';
import { Paginate } from '../../../../../../common/types/pagination';
import { Actions } from '../../../../shared/api_logic/create_api_logic';
+import {
+ CancelSyncApiActions,
+ CancelSyncApiLogic,
+} from '../../../api/connector/cancel_sync_api_logic';
import {
FetchSyncJobsApiLogic,
FetchSyncJobsArgs,
FetchSyncJobsResponse,
} from '../../../api/connector/fetch_sync_jobs_api_logic';
-import { IndexViewLogic } from '../index_view_logic';
+import { CancelSyncsLogic, CancelSyncsLogicActions } from '../connector/cancel_syncs_logic';
+import { IndexViewActions, IndexViewLogic } from '../index_view_logic';
+
+const UI_REFRESH_INTERVAL = 2000;
export interface SyncJobView extends ConnectorSyncJob {
- duration: moment.Duration;
- lastSync: string;
+ duration: moment.Duration | undefined;
+ lastSync: string | null;
}
-export interface IndexViewActions {
+export interface SyncJobsViewActions {
+ cancelSyncError: CancelSyncApiActions['apiError'];
+ cancelSyncJob: CancelSyncApiActions['makeRequest'];
+ cancelSyncSuccess: CancelSyncApiActions['apiSuccess'];
+ cancelSyncsApiError: CancelSyncsLogicActions['cancelSyncsApiError'];
+ cancelSyncsApiSuccess: CancelSyncsLogicActions['cancelSyncsApiSuccess'];
fetchSyncJobs: Actions['makeRequest'];
+ fetchSyncJobsApiSuccess: Actions['apiSuccess'];
fetchSyncJobsError: Actions['apiError'];
+ refetchSyncJobs: () => void;
+ resetCancelSyncJobApi: CancelSyncApiActions['apiReset'];
+ setCancelSyncJob: (syncJobId: ConnectorSyncJob['id'] | undefined) => {
+ syncJobId: ConnectorSyncJob['id'] | null;
+ };
+ setSelectedSyncJobCategory: (category: 'content' | 'access_control') => {
+ category: 'content' | 'access_control';
+ };
+ startAccessControlSync: IndexViewActions['startAccessControlSync'];
+ startIncrementalSync: IndexViewActions['startIncrementalSync'];
+ startSync: IndexViewActions['startSync'];
}
-export interface IndexViewValues {
+export interface SyncJobsViewValues {
+ cancelSyncJobLoading: boolean;
+ cancelSyncJobStatus: Status;
connectorId: string | null;
+ selectedSyncJobCategory: 'content' | 'access_control';
+ syncJobToCancel: ConnectorSyncJob['id'] | null;
syncJobs: SyncJobView[];
syncJobsData: Paginate | null;
syncJobsLoading: boolean;
syncJobsPagination: Pagination;
syncJobsStatus: Status;
+ syncTriggeredLocally: boolean;
}
-export const SyncJobsViewLogic = kea>({
- actions: {},
+export const SyncJobsViewLogic = kea>({
+ actions: {
+ refetchSyncJobs: true,
+ setCancelSyncJob: (syncJobId) => ({ syncJobId: syncJobId ?? null }),
+ setSelectedSyncJobCategory: (category) => ({ category }),
+ },
connect: {
actions: [
FetchSyncJobsApiLogic,
@@ -54,30 +88,137 @@ export const SyncJobsViewLogic = kea ({
+ cancelSyncError: async (_, breakpoint) => {
+ actions.resetCancelSyncJobApi();
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ cancelSyncSuccess: async (_, breakpoint) => {
+ actions.resetCancelSyncJobApi();
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ cancelSyncsApiError: async (_, breakpoint) => {
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ cancelSyncsApiSuccess: async (_, breakpoint) => {
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ refetchSyncJobs: () => {
+ if (values.connectorId) {
+ actions.fetchSyncJobs({
+ connectorId: values.connectorId,
+ from: values.syncJobsPagination.pageIndex * (values.syncJobsPagination.pageSize || 0),
+ size: values.syncJobsPagination.pageSize ?? 10,
+ type: values.selectedSyncJobCategory,
+ });
+ }
+ },
+ startAccessControlSync: async (_, breakpoint) => {
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ startIncrementalSync: async (_, breakpoint) => {
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ startSync: async (_, breakpoint) => {
+ await breakpoint(UI_REFRESH_INTERVAL);
+ if (values.connectorId) {
+ actions.refetchSyncJobs();
+ }
+ },
+ }),
+
path: ['enterprise_search', 'content', 'sync_jobs_view_logic'],
- selectors: ({ selectors }) => ({
+ reducers: {
+ selectedSyncJobCategory: [
+ 'content',
+ {
+ setSelectedSyncJobCategory: (_, { category }) => category,
+ },
+ ],
+ syncJobToCancel: [
+ null,
+ {
+ resetCancelSyncJobApi: () => null,
+ setCancelSyncJob: (_, { syncJobId }) => syncJobId ?? null,
+ },
+ ],
syncJobs: [
- () => [selectors.syncJobsData],
- (data?: Paginate) =>
- data?.data.map((syncJob) => {
- return {
- ...syncJob,
- duration: syncJob.started_at
- ? moment.duration(
- moment(syncJob.completed_at || new Date()).diff(moment(syncJob.started_at))
- )
- : undefined,
- lastSync: syncJob.completed_at,
- };
- }) ?? [],
+ [],
+ {
+ fetchSyncJobsApiSuccess: (currentState, { data }) => {
+ const newState =
+ data?.map((syncJob) => {
+ return {
+ ...syncJob,
+ duration: syncJob.started_at
+ ? moment.duration(
+ moment(syncJob.completed_at || new Date()).diff(moment(syncJob.started_at))
+ )
+ : undefined,
+ lastSync: syncJob.completed_at,
+ };
+ }) ?? [];
+
+ return isEqual(currentState, newState) ? currentState : newState;
+ },
+ },
+ ],
+ syncTriggeredLocally: [
+ false,
+ {
+ cancelSyncError: () => true,
+ cancelSyncJob: () => true,
+ cancelSyncs: () => true,
+ fetchSyncJobsApiSuccess: () => false,
+ startAccessControlSync: () => true,
+ startIncrementalSync: () => true,
+ startSync: () => true,
+ },
+ ],
+ },
+ selectors: ({ selectors }) => ({
+ cancelSyncJobLoading: [
+ () => [selectors.cancelSyncJobStatus],
+ (status: Status) => status === Status.LOADING,
],
syncJobsLoading: [
() => [selectors.syncJobsStatus],
diff --git a/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts
index 16c4feaa5626c..cad2a5cbcf43c 100644
--- a/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts
+++ b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts
@@ -130,6 +130,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -142,6 +146,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -154,6 +162,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -166,6 +178,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
last7Days: {
@@ -180,6 +196,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -192,6 +212,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -204,6 +228,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -216,6 +244,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
},
@@ -233,6 +265,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -245,6 +281,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -257,6 +297,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -269,6 +313,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
last7Days: {
@@ -283,6 +331,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -295,6 +347,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -307,6 +363,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -319,6 +379,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
},
diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts
index f445547858578..bd1498e0b0b61 100644
--- a/x-pack/plugins/enterprise_search/server/plugin.ts
+++ b/x-pack/plugins/enterprise_search/server/plugin.ts
@@ -81,6 +81,7 @@ import { workplaceSearchTelemetryType } from './saved_objects/workplace_search/t
import { GlobalConfigService } from './services/global_config_service';
import { uiSettings as enterpriseSearchUISettings } from './ui_settings';
+import { getConnectorsSearchResultProvider } from './utils/connectors_search_result_provider';
import { getIndicesSearchResultProvider } from './utils/indices_search_result_provider';
import { getSearchResultProvider } from './utils/search_result_provider';
@@ -360,6 +361,7 @@ export class EnterpriseSearchPlugin implements Plugin {
)
);
globalSearch.registerResultProvider(getIndicesSearchResultProvider(http.staticAssets));
+ globalSearch.registerResultProvider(getConnectorsSearchResultProvider(http.staticAssets));
}
}
diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
index 7ad04cf753dd7..395c114424864 100644
--- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/connectors.ts
@@ -6,11 +6,13 @@
*/
import { schema } from '@kbn/config-schema';
+
import { ElasticsearchErrorDetails } from '@kbn/es-errors';
import { i18n } from '@kbn/i18n';
import {
CONNECTORS_INDEX,
+ cancelSync,
deleteConnectorById,
deleteConnectorSecret,
fetchConnectorById,
@@ -28,7 +30,10 @@ import {
import { ConnectorStatus, FilteringRule, SyncJobType } from '@kbn/search-connectors';
import { cancelSyncs } from '@kbn/search-connectors/lib/cancel_syncs';
-import { isResourceNotFoundException } from '@kbn/search-connectors/utils/identify_exceptions';
+import {
+ isResourceNotFoundException,
+ isStatusTransitionException,
+} from '@kbn/search-connectors/utils/identify_exceptions';
import { ErrorCode } from '../../../common/types/error_codes';
import { addConnector } from '../../lib/connectors/add_connector';
@@ -117,6 +122,40 @@ export function registerConnectorRoutes({ router, log }: RouteDependencies) {
})
);
+ router.put(
+ {
+ path: '/internal/enterprise_search/connectors/{syncJobId}/cancel_sync',
+ validate: {
+ params: schema.object({
+ syncJobId: schema.string(),
+ }),
+ },
+ },
+ elasticsearchErrorHandler(log, async (context, request, response) => {
+ const { client } = (await context.core).elasticsearch;
+ try {
+ await cancelSync(client.asCurrentUser, request.params.syncJobId);
+ } catch (error) {
+ if (isStatusTransitionException(error)) {
+ return createError({
+ errorCode: ErrorCode.STATUS_TRANSITION_ERROR,
+ message: i18n.translate(
+ 'xpack.enterpriseSearch.server.routes.connectors.statusTransitionError',
+ {
+ defaultMessage:
+ 'Connector sync job cannot be cancelled. Connector is already cancelled or not in a cancelable state.',
+ }
+ ),
+ response,
+ statusCode: 400,
+ });
+ }
+ throw error;
+ }
+ return response.ok();
+ })
+ );
+
router.post(
{
path: '/internal/enterprise_search/connectors/{connectorId}/configuration',
diff --git a/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.test.ts b/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.test.ts
new file mode 100644
index 0000000000000..bb7756c0d600b
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.test.ts
@@ -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 { calculateScore } from './calculate_search_score';
+
+describe('calculateScore', () => {
+ it('should return 80 when the search term is undefined', () => {
+ const searchTerm = undefined;
+ const valueToTest = 'value to test';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(80);
+ });
+
+ it('should return 80 when the search term is empty', () => {
+ const searchTerm = '';
+ const valueToTest = 'valute to test';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(80);
+ });
+
+ it('should return 100 when the search term matches the value', () => {
+ const searchTerm = 'connector';
+ const valueToTest = 'connector';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(100);
+ });
+
+ it('should return 90 when the search term starts with the value', () => {
+ const searchTerm = 'connector';
+ const valueToTest = 'connector test';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(90);
+ });
+
+ it('should return 75 when the search term includes the value', () => {
+ const searchTerm = 'connector';
+ const valueToTest = 'test connector somewhere';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(75);
+ });
+
+ it('should return 0 when the search term does not match the value', () => {
+ const searchTerm = 'connector';
+ const valueToTest = 'index';
+ const score = calculateScore(searchTerm, valueToTest);
+ expect(score).toBe(0);
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.ts b/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.ts
new file mode 100644
index 0000000000000..ba04697421e97
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/server/utils/calculate_search_score.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.
+ */
+
+export const calculateScore = (searchTerm: string = '', valueToTest: string): number => {
+ const searchTermLower = searchTerm.toLowerCase();
+ const valueToTestLower = valueToTest.toLowerCase();
+ if (!searchTermLower) {
+ return 80;
+ } else if (valueToTestLower === searchTermLower) {
+ return 100;
+ } else if (valueToTestLower.startsWith(searchTermLower)) {
+ return 90;
+ } else if (valueToTestLower.includes(searchTermLower)) {
+ return 75;
+ }
+ return 0;
+};
diff --git a/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.test.ts b/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.test.ts
new file mode 100644
index 0000000000000..f865da1f0dcef
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.test.ts
@@ -0,0 +1,201 @@
+/*
+ * 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 { NEVER, lastValueFrom } from 'rxjs';
+
+import { IScopedClusterClient } from '@kbn/core/server';
+
+import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../common/constants';
+
+import { getConnectorsSearchResultProvider } from './connectors_search_result_provider';
+
+describe('Enterprise Search - connectors search provider', () => {
+ const staticAssetsMock = {
+ getPluginAssetHref: (input: string) => `/kbn/${input}`,
+ } as any;
+
+ const connectorsSearchResultProvider = getConnectorsSearchResultProvider(staticAssetsMock);
+
+ const connectorNameMap = {
+ mssql: {
+ id: '101',
+ name: 'companyName mssql connector aug 12 2024 rev1.2',
+ },
+ postgres: { id: '100', name: 'companyName-postgres-connector-all' },
+ spaces: { id: '102', name: 'companyName with spaces etc companyName' },
+ };
+
+ const mockConnectorResponse = {
+ results: [
+ { id: connectorNameMap.postgres.id, name: connectorNameMap.postgres.name },
+ { id: connectorNameMap.mssql.id, name: connectorNameMap.mssql.name },
+ { id: connectorNameMap.spaces.id, name: connectorNameMap.spaces.name },
+ ],
+ };
+
+ const getConnectorSearchData = (name: keyof typeof connectorNameMap) => ({
+ id: connectorNameMap[name].name,
+ score: 90,
+ title: connectorNameMap[name].name,
+ type: 'Connector',
+ url: {
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/${connectorNameMap[name].id}`,
+ prependBasePath: true,
+ },
+ icon: '/kbn/images/connector.svg',
+ });
+
+ const mockClient = {
+ asCurrentUser: {
+ transport: {
+ request: jest.fn(),
+ },
+ },
+ asInternalUser: {},
+ };
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+ const client = mockClient as unknown as IScopedClusterClient;
+ mockClient.asCurrentUser.transport.request.mockResolvedValue(mockConnectorResponse);
+
+ describe('find', () => {
+ it('returns formatted results', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'companyName-postgres-connector-all' },
+ {
+ aborted$: NEVER,
+ maxResults: 100,
+ client,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([{ ...getConnectorSearchData('postgres'), score: 100 }]);
+ });
+
+ it('returns all matched results', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'companyName' },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([
+ { ...getConnectorSearchData('postgres'), score: 90 },
+ { ...getConnectorSearchData('mssql'), score: 90 },
+ { ...getConnectorSearchData('spaces'), score: 90 },
+ ]);
+ });
+
+ it('returns all indices on empty string', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: '' },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toHaveLength(0);
+ });
+
+ it('respect maximum results', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'companyName' },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 1,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([{ ...getConnectorSearchData('postgres'), score: 90 }]);
+ });
+
+ describe('returns empty results', () => {
+ it('when term does not match with created indices', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'sample' },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([]);
+ });
+
+ it('if client is undefined', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'sample' },
+ {
+ aborted$: NEVER,
+ client: undefined,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([]);
+ });
+
+ it('if tag is specified', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'search', tags: ['tag'] },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([]);
+ });
+
+ it('if unknown type is specified', async () => {
+ const results = await lastValueFrom(
+ connectorsSearchResultProvider.find(
+ { term: 'search', types: ['tag'] },
+ {
+ aborted$: NEVER,
+ client,
+ maxResults: 100,
+ preference: '',
+ },
+ {} as any
+ )
+ );
+ expect(results).toEqual([]);
+ });
+ });
+ });
+});
diff --git a/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.ts b/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.ts
new file mode 100644
index 0000000000000..3035a136f34de
--- /dev/null
+++ b/x-pack/plugins/enterprise_search/server/utils/connectors_search_result_provider.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 { from, takeUntil } from 'rxjs';
+
+import { IStaticAssets } from '@kbn/core-http-browser';
+
+import {
+ GlobalSearchProviderResult,
+ GlobalSearchResultProvider,
+} from '@kbn/global-search-plugin/server';
+import { i18n } from '@kbn/i18n';
+
+import { fetchConnectors } from '@kbn/search-connectors';
+
+import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../common/constants';
+
+import { calculateScore } from './calculate_search_score';
+
+export function getConnectorsSearchResultProvider(
+ staticAssets: IStaticAssets
+): GlobalSearchResultProvider {
+ return {
+ find: ({ term, types, tags }, { aborted$, client, maxResults }) => {
+ if (!client || !term || tags || (types && !types.includes('connectors'))) {
+ return from([[]]);
+ }
+ const getConnectorData = async (): Promise => {
+ const connectorData = await fetchConnectors(client.asCurrentUser, undefined, false, term);
+ const searchResults: GlobalSearchProviderResult[] = connectorData
+ .map(({ name, id }) => {
+ const score = calculateScore(term, name);
+ return {
+ icon: staticAssets.getPluginAssetHref('images/connector.svg'),
+ id: name,
+ score,
+ title: name,
+ type: i18n.translate('xpack.enterpriseSearch.searchConnectorProvider.type.name', {
+ defaultMessage: 'Connector',
+ }),
+ url: {
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/${id}`,
+ prependBasePath: true,
+ },
+ };
+ })
+ .filter((result) => result.score > 0)
+ .slice(0, maxResults);
+ return searchResults;
+ };
+ return from(getConnectorData()).pipe(takeUntil(aborted$));
+ },
+ getSearchableTypes: () => ['connectors'],
+ id: 'enterpriseSearchConnectors',
+ };
+}
diff --git a/x-pack/plugins/enterprise_search/server/utils/indices_search_result_provider.ts b/x-pack/plugins/enterprise_search/server/utils/indices_search_result_provider.ts
index 478ee92d69f4e..295441e125aa4 100644
--- a/x-pack/plugins/enterprise_search/server/utils/indices_search_result_provider.ts
+++ b/x-pack/plugins/enterprise_search/server/utils/indices_search_result_provider.ts
@@ -18,6 +18,8 @@ import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../common/constants';
import { getIndexData } from '../lib/indices/utils/get_index_data';
+import { calculateScore } from './calculate_search_score';
+
export function getIndicesSearchResultProvider(
staticAssets: IStaticAssets
): GlobalSearchResultProvider {
@@ -31,18 +33,7 @@ export function getIndicesSearchResultProvider(
const searchResults: GlobalSearchProviderResult[] = indexNames
.map((indexName) => {
- let score = 0;
- const searchTerm = (term || '').toLowerCase();
- const searchName = indexName.toLowerCase();
- if (!searchTerm) {
- score = 80;
- } else if (searchName === searchTerm) {
- score = 100;
- } else if (searchName.startsWith(searchTerm)) {
- score = 90;
- } else if (searchName.includes(searchTerm)) {
- score = 75;
- }
+ const score = calculateScore(term, indexName);
return {
id: indexName,
diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts
index 9f9966b5e5998..7175f6135e84c 100644
--- a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts
+++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.test.ts
@@ -55,7 +55,7 @@ describe('Enterprise Search search provider', () => {
title: 'Elastic Web Crawler',
type: 'Search',
url: {
- path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/crawler`,
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/crawlers/new_crawler`,
prependBasePath: true,
},
};
@@ -67,7 +67,7 @@ describe('Enterprise Search search provider', () => {
title: 'MongoDB',
type: 'Search',
url: {
- path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/connector?connector_type=connector_client&service_type=mongodb`,
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/new_connector?connector_type=connector_client&service_type=mongodb`,
prependBasePath: true,
},
};
@@ -79,7 +79,7 @@ describe('Enterprise Search search provider', () => {
title: 'MongoDB',
type: 'Search',
url: {
- path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/connector?connector_type=native&service_type=mongodb`,
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/new_connector?connector_type=native&service_type=mongodb`,
prependBasePath: true,
},
};
@@ -91,7 +91,7 @@ describe('Enterprise Search search provider', () => {
title: 'Customized connector',
type: 'Search',
url: {
- path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/connector?connector_type=connector_client&service_type=`,
+ path: `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/new_connector?connector_type=connector_client&service_type=`,
prependBasePath: true,
},
};
diff --git a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts
index e7320f21768a5..d4791aa80fe45 100644
--- a/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts
+++ b/x-pack/plugins/enterprise_search/server/utils/search_result_provider.ts
@@ -54,6 +54,9 @@ export function toSearchResult({
? 'native'
: 'connector_client'
: null;
+ const newUrl = isCrawler
+ ? `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/crawlers/new_crawler`
+ : `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/connectors/new_connector?connector_type=${connectorTypeParam}&service_type=${serviceType}`;
return {
icon: iconPath || 'logoEnterpriseSearch',
@@ -64,13 +67,7 @@ export function toSearchResult({
defaultMessage: 'Search',
}),
url: {
- path:
- url ??
- `${ENTERPRISE_SEARCH_CONTENT_PLUGIN.URL}/search_indices/new_index/${
- isCrawler
- ? 'crawler'
- : `connector?connector_type=${connectorTypeParam}&service_type=${serviceType}`
- }`,
+ path: url ?? newUrl,
prependBasePath: true,
},
};
diff --git a/x-pack/plugins/fleet/common/constants/agent_policy.ts b/x-pack/plugins/fleet/common/constants/agent_policy.ts
index a52a0cdc7bfcb..b2cbed6bb68e9 100644
--- a/x-pack/plugins/fleet/common/constants/agent_policy.ts
+++ b/x-pack/plugins/fleet/common/constants/agent_policy.ts
@@ -37,3 +37,19 @@ export const LICENSE_FOR_SCHEDULE_UPGRADE = 'platinum';
export const DEFAULT_MAX_AGENT_POLICIES_WITH_INACTIVITY_TIMEOUT = 750;
export const AGENTLESS_POLICY_ID = 'agentless'; // the policy id defined here: https://github.com/elastic/project-controller/blob/main/internal/project/security/security_kibana_config.go#L86
+
+export const AGENT_LOG_LEVELS = {
+ ERROR: 'error',
+ WARNING: 'warning',
+ INFO: 'info',
+ DEBUG: 'debug',
+};
+
+export const DEFAULT_LOG_LEVEL = AGENT_LOG_LEVELS.INFO;
+
+export const agentLoggingLevels = {
+ Info: 'info',
+ Debug: 'debug',
+ Warning: 'warning',
+ Error: 'error',
+} as const;
diff --git a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts b/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
index ac5c57f9107c5..9d23f948da3e6 100644
--- a/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
+++ b/x-pack/plugins/fleet/common/settings/agent_policy_settings.ts
@@ -8,6 +8,8 @@
import { i18n } from '@kbn/i18n';
import { z } from 'zod';
+import { agentLoggingLevels } from '../constants';
+
import type { SettingsConfig } from './types';
export const zodStringWithDurationValidation = z
@@ -126,4 +128,21 @@ export const AGENT_POLICY_ADVANCED_SETTINGS: SettingsConfig[] = [
})
.default({}),
},
+ {
+ name: 'agent.logging.level',
+ hidden: true,
+ title: i18n.translate('xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelTitle', {
+ defaultMessage: 'Agent Logging Level',
+ }),
+ description: i18n.translate(
+ 'xpack.fleet.settings.agentPolicyAdvanced.agentLoggingLevelDescription',
+ {
+ defaultMessage: 'Set the Agent log level. The default log level is "info".',
+ }
+ ),
+ api_field: {
+ name: 'agent_logging_level',
+ },
+ schema: z.nativeEnum(agentLoggingLevels),
+ },
];
diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
index 2836f1a2c8c38..ce725773a462f 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/components/form_settings/index.tsx
@@ -7,7 +7,7 @@
import { ZodFirstPartyTypeKind } from 'zod';
import React from 'react';
-import { EuiFieldNumber, EuiFieldText } from '@elastic/eui';
+import { EuiFieldNumber, EuiFieldText, EuiSelect } from '@elastic/eui';
import type { SettingsConfig } from '../../../../../common/settings/types';
@@ -61,6 +61,27 @@ settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodString, (settingsConfig) =
);
});
+settingComponentRegistry.set(ZodFirstPartyTypeKind.ZodNativeEnum, (settingsConfig) => {
+ return (
+ (
+ ({
+ text: level,
+ value: coercedSchema.enum[level],
+ }))}
+ />
+ )}
+ />
+ );
+});
+
export function ConfiguredSettings({
configuredSettings,
}: {
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
index 02795f8005bf5..08049f6d81669 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/index.tsx
@@ -63,7 +63,6 @@ interface Props {
agentPolicy: Partial;
updateAgentPolicy: (u: Partial) => void;
validation: ValidationResults;
- isEditing?: boolean;
disabled?: boolean;
}
@@ -71,7 +70,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent =
agentPolicy,
updateAgentPolicy,
validation,
- isEditing = false,
disabled = false,
}) => {
const { docLinks } = useStartServices();
@@ -401,7 +399,6 @@ export const AgentPolicyAdvancedOptionsContent: React.FunctionComponent =
}}
/>
-
{AgentTamperProtectionSection}
= ({
agentPolicy={newAgentPolicy}
updateAgentPolicy={updateNewAgentPolicy}
validation={validation}
- isEditing={false}
/>
>
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx
index 8a0d92b159207..97fc1fc28acf9 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_form.tsx
@@ -141,7 +141,6 @@ export const AgentPolicyForm: React.FunctionComponent = ({
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
validation={validation}
- isEditing={isEditing}
/>
{advancedPolicySettings ? (
@@ -168,7 +167,6 @@ export const AgentPolicyForm: React.FunctionComponent = ({
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
validation={validation}
- isEditing={isEditing}
disabled={disabled}
/>
{advancedPolicySettings ? (
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx
index 759c85b54a181..5de23cd555198 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_integration.tsx
@@ -35,8 +35,6 @@ interface Props {
withSysMonitoring: boolean;
updateSysMonitoring: (newValue: boolean) => void;
validation: ValidationResults;
- isEditing?: boolean;
- onDelete?: () => void;
}
export const AgentPolicyIntegrationForm: React.FunctionComponent = ({
@@ -45,8 +43,6 @@ export const AgentPolicyIntegrationForm: React.FunctionComponent = ({
withSysMonitoring,
updateSysMonitoring,
validation,
- isEditing = false,
- onDelete = () => {},
}) => {
return (
@@ -101,7 +97,6 @@ export const AgentPolicyIntegrationForm: React.FunctionComponent = ({
agentPolicy={agentPolicy}
updateAgentPolicy={updateAgentPolicy}
validation={validation}
- isEditing={isEditing}
/>
>
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx
index bee26f7c5b067..c15c722f7aa4b 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/constants.tsx
@@ -48,12 +48,3 @@ export const DEFAULT_LOGS_STATE: AgentLogsState = {
export const STATE_STORAGE_KEY = '_q';
export const STATE_DATASET_FIELD = 'datasets';
-
-export const AGENT_LOG_LEVELS = {
- ERROR: 'error',
- WARNING: 'warning',
- INFO: 'info',
- DEBUG: 'debug',
-};
-
-export const DEFAULT_LOG_LEVEL = AGENT_LOG_LEVELS.INFO;
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
index b4971ec4b2209..e565ddccc4b70 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/filter_log_level.tsx
@@ -10,7 +10,7 @@ import type { EuiSelectableOption } from '@elastic/eui';
import { EuiPopover, EuiFilterButton, EuiSelectable } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { AGENT_LOG_LEVELS } from './constants';
+import { AGENT_LOG_LEVELS } from '../../../../../../../../common/constants';
const LEVEL_VALUES = Object.values(AGENT_LOG_LEVELS);
diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx
index c442925960977..4c32952339347 100644
--- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx
+++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_logs/select_log_level.tsx
@@ -13,7 +13,7 @@ import { EuiSelect, EuiFormLabel, EuiButtonEmpty, EuiFlexItem, EuiFlexGroup } fr
import type { Agent } from '../../../../../types';
import { sendPostAgentAction, useAuthz, useStartServices } from '../../../../../hooks';
-import { AGENT_LOG_LEVELS, DEFAULT_LOG_LEVEL } from './constants';
+import { AGENT_LOG_LEVELS, DEFAULT_LOG_LEVEL } from '../../../../../../../../common/constants';
const LEVEL_VALUES = Object.values(AGENT_LOG_LEVELS);
diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts
index 3a56bf248bcf4..c85a211fb49a8 100644
--- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts
+++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts
@@ -719,6 +719,7 @@ describe('getFullAgentPolicy', () => {
mockAgentPolicy({
advanced_settings: {
agent_limits_go_max_procs: 2,
+ agent_logging_level: 'debug',
},
});
const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy');
@@ -727,6 +728,7 @@ describe('getFullAgentPolicy', () => {
id: 'agent-policy',
agent: {
limits: { go_max_procs: 2 },
+ logging: { level: 'debug' },
},
});
});
diff --git a/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts b/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts
index c0ac25ff0627d..e1fbd25b824ab 100644
--- a/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts
+++ b/x-pack/plugins/global_search_bar/public/lib/result_to_option.test.ts
@@ -61,6 +61,15 @@ describe('resultToOption', () => {
);
});
+ it('uses icon for `connector` type', () => {
+ const input = createSearchResult({ type: 'connector', icon: 'connector-icon' });
+ expect(resultToOption(input, [])).toEqual(
+ expect.objectContaining({
+ icon: { type: 'connector-icon' },
+ })
+ );
+ });
+
it('does not use icon for other types', () => {
const input = createSearchResult({ type: 'dashboard', icon: 'dash-icon' });
expect(resultToOption(input, [])).toEqual(
diff --git a/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx b/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx
index 555434a85404f..1e5de6bf3b788 100644
--- a/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx
+++ b/x-pack/plugins/global_search_bar/public/lib/result_to_option.tsx
@@ -26,7 +26,8 @@ export const resultToOption = (
type === 'integration' ||
type.toLowerCase() === 'enterprise search' ||
type.toLowerCase() === 'search' ||
- type.toLowerCase() === 'index';
+ type.toLowerCase() === 'index' ||
+ type.toLowerCase() === 'connector';
const option: EuiSelectableTemplateSitewideOption = {
key: id,
label: title,
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 bc160ab4bcd57..d15723e074667 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
@@ -724,12 +724,18 @@ describe('Textbased Data Source', () => {
},
Object {
"arguments": Object {
+ "descriptionForInspector": Array [
+ "This request queries Elasticsearch to fetch the data for the visualization.",
+ ],
"locale": Array [
"en",
],
"query": Array [
"FROM foo",
],
+ "titleForInspector": Array [
+ "Visualization",
+ ],
},
"function": "esql",
"type": "function",
diff --git a/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts b/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts
index 9d7be5d2fb431..148a16232c980 100644
--- a/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts
+++ b/x-pack/plugins/lens/public/datasources/text_based/to_expression.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { i18n } from '@kbn/i18n';
import { Ast } from '@kbn/interpreter';
import { textBasedQueryStateToExpressionAst } from '@kbn/data-plugin/common';
import type { OriginalColumn } from '../../../common/types';
@@ -44,6 +45,13 @@ function getExpressionForLayer(
const textBasedQueryToAst = textBasedQueryStateToExpressionAst({
query: layer.query,
timeFieldName,
+ titleForInspector: i18n.translate('xpack.lens.inspectorTextBasedRequestDataTitle', {
+ defaultMessage: 'Visualization',
+ }),
+ descriptionForInspector: i18n.translate('xpack.lens.inspectorTextBasedRequestDescription', {
+ defaultMessage:
+ 'This request queries Elasticsearch to fetch the data for the visualization.',
+ }),
});
textBasedQueryToAst.chain.push({
diff --git a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts
index 594b85afef135..a73c9f274b40b 100644
--- a/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts
+++ b/x-pack/plugins/metrics_data_access/common/inventory_models/host/metrics/charts/disk.ts
@@ -16,8 +16,8 @@ import {
DEFAULT_XY_YBOUNDS,
} from '../../../shared/charts/constants';
-const diskThroughputReadWrite: LensConfigWithId = {
- id: 'diskThroughputReadWrite',
+const diskIOReadWrite: LensConfigWithId = {
+ id: 'diskIOReadWrite',
chartType: 'xy',
title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskIOPS', {
defaultMessage: 'Disk IOPS',
@@ -83,8 +83,8 @@ const diskUsageByMountPoint: LensConfigWithId = {
...DEFAULT_XY_HIDDEN_AXIS_TITLE,
};
-const diskIOReadWrite: LensConfigWithId = {
- id: 'diskIOReadWrite',
+const diskThroughputReadWrite: LensConfigWithId = {
+ id: 'diskThroughputReadWrite',
chartType: 'xy',
title: i18n.translate('xpack.metricsData.assetDetails.metricsCharts.diskThroughput', {
defaultMessage: 'Disk Throughput',
diff --git a/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx b/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx
index 8b5b099498cc8..bcc876295973f 100644
--- a/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/common/asset_details_config/asset_details_tabs.tsx
@@ -13,43 +13,49 @@ import { ContentTabIds, type Tab } from '../../components/asset_details/types';
export const commonFlyoutTabs: Tab[] = [
{
id: ContentTabIds.OVERVIEW,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.overview', {
defaultMessage: 'Overview',
}),
},
{
id: ContentTabIds.METADATA,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.metadata', {
defaultMessage: 'Metadata',
}),
},
+ {
+ id: ContentTabIds.METRICS,
+ name: i18n.translate('xpack.infra.assetDetails.tabs.metrics', {
+ defaultMessage: 'Metrics',
+ }),
+ },
{
id: ContentTabIds.PROCESSES,
- name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.processes', {
defaultMessage: 'Processes',
}),
},
{
id: ContentTabIds.PROFILING,
- name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.profiling', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.profiling', {
defaultMessage: 'Universal Profiling',
}),
},
{
id: ContentTabIds.LOGS,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.logs.title', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.logs', {
defaultMessage: 'Logs',
}),
},
{
id: ContentTabIds.ANOMALIES,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.anomalies', {
defaultMessage: 'Anomalies',
}),
},
{
id: ContentTabIds.OSQUERY,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.osquery', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.osquery', {
defaultMessage: 'Osquery',
}),
},
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 8cca6dd66d027..7e76b3fa1474b 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
@@ -12,37 +12,37 @@ const links: AssetDetailsProps['links'] = ['alertRule', 'nodeDetails'];
const tabs: Tab[] = [
{
id: ContentTabIds.OVERVIEW,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.overview.title', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.overview', {
defaultMessage: 'Overview',
}),
},
{
id: ContentTabIds.METADATA,
- name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.metadata', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.metadata', {
defaultMessage: 'Metadata',
}),
},
{
id: ContentTabIds.PROCESSES,
- name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.processes', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.processes', {
defaultMessage: 'Processes',
}),
},
{
id: ContentTabIds.LOGS,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.logs', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.logs', {
defaultMessage: 'Logs',
}),
},
{
id: ContentTabIds.ANOMALIES,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.anomalies', {
defaultMessage: 'Anomalies',
}),
},
{
id: ContentTabIds.LINK_TO_APM,
- name: i18n.translate('xpack.infra.infra.nodeDetails.apmTabLabel', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.apmLink', {
defaultMessage: 'APM',
}),
},
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx
similarity index 64%
rename from x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx
rename to x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx
index db61602279306..a1edbfa327a4b 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/chart.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/chart.tsx
@@ -8,45 +8,36 @@ import React, { useCallback, useMemo } 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 { METRIC_CHART_HEIGHT } from '../../../../../common/visualizations/constants';
-import { buildCombinedHostsFilter } from '../../../../../utils/filters/build';
-import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../../../lens';
-import { useDatePickerContext } from '../../../hooks/use_date_picker';
+import { useDataView } from '../../../hooks/use_data_view';
+import { METRIC_CHART_HEIGHT } from '../../../common/visualizations/constants';
+import { buildCombinedHostsFilter } from '../../../utils/filters/build';
+import { type BrushEndArgs, LensChart, type OnFilterEvent, LensChartProps } from '../../lens';
+import { useDatePickerContext } from '../hooks/use_date_picker';
import { extractRangeFromChartFilterEvent } from './chart_utils';
-import { useLoadingStateContext } from '../../../hooks/use_loading_state';
+import { useLoadingStateContext } from '../hooks/use_loading_state';
export type ChartProps = LensConfig &
Pick & {
id: string;
- filterFieldName: string;
+ queryField: string;
dateRange: TimeRange;
- assetName: string;
- ['data-test-subj']: string;
+ assetId: string;
};
-export const Chart = ({
- id,
- filterFieldName,
- overrides,
- dateRange,
- assetName,
- ...props
-}: ChartProps) => {
+export const Chart = ({ id, queryField, overrides, dateRange, assetId, ...props }: ChartProps) => {
const { setDateRange } = useDatePickerContext();
const { searchSessionId } = useLoadingStateContext();
- const { ['data-test-subj']: dataTestSubj, ...chartProps } = { ...props };
- const { dataView } = useDataView({ index: (chartProps.dataset as LensDataviewDataset)?.index });
+ const { dataView } = useDataView({ index: (props.dataset as LensDataviewDataset)?.index });
const filters = useMemo(() => {
return [
buildCombinedHostsFilter({
- field: filterFieldName,
- values: [assetName],
+ field: queryField,
+ values: [assetId],
dataView,
}),
];
- }, [assetName, dataView, filterFieldName]);
+ }, [assetId, dataView, queryField]);
const handleBrushEnd = useCallback(
({ range, preventDefault }: BrushEndArgs) => {
@@ -76,8 +67,8 @@ export const Chart = ({
return (
;
+ onShowAll?: (metric: string) => void;
+}
+
+const FRAGMENT_BASE = 'key-metrics';
+
+export const HostCharts = React.forwardRef(
+ ({ assetId, dataView, dateRange, metric, onShowAll, overview = false }, ref) => {
+ const { charts } = useHostCharts({
+ metric,
+ dataViewId: dataView?.id,
+ options: { overview },
+ });
+
+ return (
+
+
+
+
+ ),
+ }}
+ />
+
+ }
+ />
+ }
+ data-test-subj={`infraAssetDetailsHostChartsSection${metric}`}
+ id={metric}
+ ref={ref}
+ extraAction={
+ onShowAll ? (
+ onShowAll(metric)}
+ size="xs"
+ flush="both"
+ iconSide="right"
+ iconType="sortRight"
+ >
+
+
+ ) : null
+ }
+ >
+
+ {charts.map((chart) => (
+
+ ))}
+
+
+ );
+ }
+);
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/index.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx
similarity index 71%
rename from x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/index.ts
rename to x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx
index 86370222898e3..180a5558e29bd 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/index.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/index.tsx
@@ -5,5 +5,5 @@
* 2.0.
*/
-export * from './hosts';
-export * from './unique_ips';
+export { HostCharts } from './host_charts';
+export { KubernetesCharts } from './kubernetes_charts';
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.tsx
new file mode 100644
index 0000000000000..8791b351bf7e7
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts/kubernetes_charts.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 React from 'react';
+import { EuiButtonEmpty } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n-react';
+import type { TimeRange } from '@kbn/es-query';
+import type { DataView } from '@kbn/data-views-plugin/public';
+import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
+import { useKubernetesCharts } from '../hooks/use_metrics_charts';
+import { Section } from '../components/section';
+import { SectionTitle } from '../components/section_title';
+import { HOST_METRIC_GROUP_TITLES } from '../translations';
+import { INTEGRATIONS } from '../constants';
+import { ChartsGrid } from '../charts_grid/charts_grid';
+import { Chart } from './chart';
+import { useIntegrationCheck } from '../hooks/use_integration_check';
+
+interface Props {
+ assetId: string;
+ dateRange: TimeRange;
+ dataView?: DataView;
+ overview?: boolean;
+ onShowAll?: (metric: string) => void;
+}
+
+export const KubernetesCharts = React.forwardRef<
+ HTMLDivElement,
+ Props & { onShowAll?: (metric: string) => void }
+>(({ assetId, dataView, dateRange, onShowAll, overview }, ref) => {
+ const { charts } = useKubernetesCharts({
+ dataViewId: dataView?.id,
+ options: { overview },
+ });
+
+ const hasIntegration = useIntegrationCheck({ dependsOn: INTEGRATIONS.kubernetes });
+
+ if (!hasIntegration) {
+ return null;
+ }
+
+ return (
+ }
+ data-test-subj="infraAssetDetailsKubernetesChartsSection"
+ id="kubernetes"
+ ref={ref}
+ extraAction={
+ onShowAll ? (
+ onShowAll('kubernetes')}
+ size="xs"
+ flush="both"
+ iconSide="right"
+ iconType="sortRight"
+ >
+
+
+ ) : null
+ }
+ >
+
+ {charts.map((chart) => (
+
+ ))}
+
+
+ );
+});
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts_grid/charts_grid.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts_grid/charts_grid.tsx
new file mode 100644
index 0000000000000..03b5ce8293ae3
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/charts_grid/charts_grid.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { EuiFlexGroup, EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
+import React from 'react';
+
+export const ChartsGrid = ({
+ children,
+ columns,
+}: {
+ children: React.ReactNode;
+ columns: 1 | 2 | 3 | 4;
+}) => {
+ const childrenArray = React.Children.toArray(children);
+ const childrenCount = childrenArray.length;
+
+ const { grid, lastRow } = React.useMemo(() => {
+ const isOddLength = childrenCount % columns !== 0;
+ const gridItems = isOddLength ? childrenArray.slice(0, -1) : childrenArray;
+ const lastRowItem = isOddLength ? childrenArray[childrenCount - 1] : undefined;
+
+ return { grid: gridItems, lastRow: lastRowItem };
+ }, [childrenArray, childrenCount, columns]);
+
+ if (childrenCount === 1) {
+ return <>{children}>;
+ }
+
+ return (
+
+
+ {grid.map((item, index) => (
+ {item}
+ ))}
+
+ {lastRow && {lastRow} }
+
+ );
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/expandable_content.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/expandable_content.tsx
index b5ef833c13c66..1d96dd143b01b 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/expandable_content.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/expandable_content.tsx
@@ -37,7 +37,7 @@ export const ExpandableContent = (props: ExpandableContentProps) => {
onClick={toggle}
>
{
{hasOthers && isExpanded && (
- {i18n.translate('xpack.infra.nodeDetails.tabs.metadata.seeLess', {
+ {i18n.translate('xpack.infra.assetDetails.tabs.metadata.seeLess', {
defaultMessage: 'Show less',
})}
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section.tsx
new file mode 100644
index 0000000000000..e8c30b99d9b14
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section.tsx
@@ -0,0 +1,105 @@
+/*
+ * 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, { useEffect, useState } from 'react';
+
+import {
+ EuiAccordion,
+ EuiFlexGroup,
+ EuiFlexItem,
+ useGeneratedHtmlId,
+ type EuiAccordionProps,
+} from '@elastic/eui';
+
+interface Props {
+ title: React.ReactNode;
+ closedSectionContent?: React.ReactNode;
+ extraAction?: React.ReactNode;
+ children: React.ReactNode;
+ collapsible?: boolean;
+ ['data-test-subj']: string;
+ id: string;
+ initialTriggerValue?: EuiAccordionProps['forceState'];
+}
+
+export const Section = React.forwardRef(
+ (
+ {
+ title,
+ closedSectionContent,
+ extraAction,
+ children,
+ collapsible = false,
+ ['data-test-subj']: dataTestSubj,
+ id,
+ initialTriggerValue,
+ },
+ ref
+ ) => {
+ const [trigger, setTrigger] = useState('open');
+
+ useEffect(() => {
+ setTrigger(initialTriggerValue ?? 'open');
+ }, [initialTriggerValue]);
+
+ const ButtonContent = () =>
+ closedSectionContent && trigger === 'closed' ? (
+
+ {title}
+ {closedSectionContent}
+
+ ) : (
+ <>{title}>
+ );
+ const collapsibleSectionAccordionId = useGeneratedHtmlId({
+ prefix: id,
+ });
+
+ const onToggle = (isOpen: boolean) => {
+ setTrigger(isOpen ? 'open' : 'closed');
+ };
+
+ return collapsible ? (
+
+ }
+ buttonProps={{ 'data-test-subj': dataTestSubj }}
+ paddingSize="s"
+ initialIsOpen
+ extraAction={extraAction}
+ forceState={trigger}
+ onToggle={onToggle}
+ data-section-state={trigger}
+ data-test-subj="infraAssetDetailsCollapseExpandSection"
+ >
+ {children}
+
+
+ ) : (
+
+
+ {title}
+ {extraAction && {extraAction} }
+
+ {React.Children.toArray(children).filter(Boolean).length > 0 ? (
+ {children}
+ ) : null}
+
+ );
+ }
+);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_title.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_title.tsx
new file mode 100644
index 0000000000000..d0ff2079a5a18
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_title.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 React, { type ReactNode } from 'react';
+
+import { EuiFlexItem, EuiTitle, EuiFlexGroup } from '@elastic/eui';
+import { Popover } from '../tabs/common/popover';
+
+export const SectionTitle = ({
+ title,
+ 'data-test-subj': dataTestSubject,
+}: {
+ title: React.ReactNode;
+ 'data-test-subj'?: string;
+}) => {
+ return (
+
+ {title}
+
+ );
+};
+
+export const TitleWithTooltip = ({
+ title,
+ 'data-test-subj': dataTestSubject,
+ tooltipTestSubj,
+ tooltipContent,
+}: {
+ title: string;
+ tooltipContent: ReactNode;
+ 'data-test-subj'?: string;
+ tooltipTestSubj?: string;
+}) => {
+ return (
+
+
+
+
+
+
+ {tooltipContent}
+
+
+
+ );
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_titles.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_titles.tsx
deleted file mode 100644
index 7067909d56ee9..0000000000000
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/components/section_titles.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import React, { type ReactNode } from 'react';
-
-import { EuiFlexItem, EuiTitle, EuiFlexGroup } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
-import { HostMetricsExplanationContent } from '../../lens';
-import { Popover } from '../tabs/common/popover';
-import { AlertsTooltipContent } from './alerts_tooltip_content';
-import { ServicesTooltipContent } from './services_tooltip_content';
-
-const SectionTitle = ({
- title,
- 'data-test-subj': dataTestSubject,
-}: {
- title: string;
- 'data-test-subj'?: string;
-}) => {
- return (
-
- {title}
-
- );
-};
-
-const TitleWithTooltip = ({
- title,
- 'data-test-subj': dataTestSubject,
- tooltipTestSubj,
- children,
-}: {
- title: string;
- children: ReactNode;
- 'data-test-subj'?: string;
- tooltipTestSubj?: string;
-}) => {
- return (
-
-
-
-
-
-
- {children}
-
-
-
- );
-};
-
-export const MetricsSectionTitle = () => {
- return (
-
-
-
- );
-};
-
-export const KubernetesMetricsSectionTitle = () => (
-
-);
-
-export const MetadataSectionTitle = () => (
-
-);
-
-export const AlertsSectionTitle = () => {
- return (
-
-
-
- );
-};
-
-export const ServicesSectionTitle = () => (
-
-
-
-);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts
index 4e3ed09e1d46b..4c66628324c50 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/constants.ts
@@ -15,5 +15,5 @@ export const APM_HOST_FILTER_FIELD = 'host.hostname';
export const ASSET_DETAILS_URL_STATE_KEY = 'assetDetails';
export const INTEGRATIONS = {
- [INTEGRATION_NAME.kubernetes]: ['kubernetes.node'],
+ [INTEGRATION_NAME.kubernetes]: 'kubernetes.node',
};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/content/content.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/content/content.tsx
index 6524f34252fa6..52bff06e75a33 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/content/content.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/content/content.tsx
@@ -6,6 +6,7 @@
*/
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { capitalize } from 'lodash';
import React from 'react';
import { DatePicker } from '../date_picker/date_picker';
import { useTabSwitcherContext } from '../hooks/use_tab_switcher';
@@ -14,6 +15,7 @@ import {
Dashboards,
Logs,
Metadata,
+ Metrics,
Osquery,
Overview,
Processes,
@@ -30,6 +32,7 @@ export const Content = () => {
ContentTabIds.OVERVIEW,
ContentTabIds.LOGS,
ContentTabIds.METADATA,
+ ContentTabIds.METRICS,
ContentTabIds.PROCESSES,
ContentTabIds.ANOMALIES,
ContentTabIds.DASHBOARDS,
@@ -49,6 +52,9 @@ export const Content = () => {
+
+
+
@@ -86,6 +92,11 @@ const TabPanel = ({
const { renderedTabsSet, activeTabId } = useTabSwitcherContext();
return renderedTabsSet.current.has(activeWhen) ? (
- {children}
+
+ {children}
+
) : null;
};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
index fc617292ad4e9..294df29acab87 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_asset_details_url_state.ts
@@ -54,6 +54,7 @@ export const useAssetDetailsUrlState = (): [AssetDetailsUrl, SetAssetDetailsStat
const TabIdRT = rt.union([
rt.literal(ContentTabIds.OVERVIEW),
rt.literal(ContentTabIds.METADATA),
+ rt.literal(ContentTabIds.METRICS),
rt.literal(ContentTabIds.PROCESSES),
rt.literal(ContentTabIds.PROFILING),
rt.literal(ContentTabIds.LOGS),
@@ -69,6 +70,18 @@ const AlertStatusRT = rt.union([
rt.literal(ALERT_STATUS_UNTRACKED),
]);
+interface TabIdWithSectionBrand {
+ readonly TabIdWithSection: unique symbol;
+}
+
+// Custom codec for tabId with fragment
+const TabIdWithSectionRT = rt.brand(
+ rt.string,
+ (s): s is rt.Branded =>
+ s.includes('#') ? TabIdRT.is(s.split('#')[0]) : TabIdRT.is(s),
+ 'TabIdWithSection'
+);
+
const AssetDetailsUrlStateRT = rt.partial({
autoRefresh: rt.partial({
isPaused: rt.boolean,
@@ -78,7 +91,7 @@ const AssetDetailsUrlStateRT = rt.partial({
from: rt.string,
to: rt.string,
}),
- tabId: TabIdRT,
+ tabId: TabIdWithSectionRT,
name: rt.string,
processSearch: rt.string,
metadataSearch: rt.string,
@@ -90,7 +103,10 @@ const AssetDetailsUrlStateRT = rt.partial({
const AssetDetailsUrlRT = rt.union([AssetDetailsUrlStateRT, rt.null]);
-export type AssetDetailsUrlState = rt.TypeOf;
+export type AssetDetailsUrlState = Omit, 'tabId'> & {
+ tabId?: string;
+};
+
type AssetDetailsUrl = rt.TypeOf;
type Payload = Partial;
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_integration_check.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_integration_check.ts
new file mode 100644
index 0000000000000..000fee4705828
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_integration_check.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 { useMemo } from 'react';
+import { useMetadataStateContext } from './use_metadata_state';
+
+export const useIntegrationCheck = ({ dependsOn }: { dependsOn: string }) => {
+ const { metadata } = useMetadataStateContext();
+
+ const hasIntegration = useMemo(
+ () => (metadata?.features ?? []).some((f) => f.name === dependsOn),
+ [metadata?.features, dependsOn]
+ );
+
+ return hasIntegration;
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts
index 863f220fcca3e..1e0573e187074 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.test.ts
@@ -6,99 +6,102 @@
*/
import { renderHook } from '@testing-library/react-hooks';
-import type { LensXYConfig } from '@kbn/lens-embeddable-utils/config_builder';
import {
- useHostFlyoutViewMetricsCharts,
useHostKpiCharts,
- useHostPageViewMetricsCharts,
- useKubernetesSectionMetricsCharts,
+ useHostCharts,
+ useKubernetesCharts,
+ type HostMetricTypes,
} from './use_metrics_charts';
-const metricsDataViewId = 'metricsDataViewId';
-const logsDataViewId = 'logsDataViewId';
-
-describe('useHostFlyoutViewMetricsCharts', () => {
- it('should return an array of charts with correct order', async () => {
- const { result, waitForNextUpdate } = renderHook(() =>
- useHostFlyoutViewMetricsCharts({ metricsDataViewId, logsDataViewId })
- );
- await waitForNextUpdate();
-
- const expectedOrder = [
- 'cpuUsage',
- 'memoryUsage',
- 'normalizedLoad1m',
- 'logRate',
- 'diskUsageByMountPoint',
- 'diskThroughputReadWrite',
- 'diskIOReadWrite',
- 'rxTx',
- ];
-
- expect(result.current).toHaveLength(expectedOrder.length);
-
- result.current.forEach((chart, index) => {
- expect(chart).toHaveProperty('id', expectedOrder[index]);
- });
- });
-
- it('should return a chart with id "logRate" using the logsDataViewId', async () => {
- const { result, waitForNextUpdate } = renderHook(() =>
- useHostFlyoutViewMetricsCharts({ metricsDataViewId, logsDataViewId })
- );
- await waitForNextUpdate();
-
- const logRateChart = result.current.find((chart) => chart.id === 'logRate') as LensXYConfig;
- expect(logRateChart).toBeDefined();
- expect(logRateChart.dataset).toHaveProperty('index', logsDataViewId);
- });
+const dataViewId = 'metricsDataViewId';
+const getHostChartsExpectedOrder = (metric: HostMetricTypes, overview: boolean): string[] => {
+ switch (metric) {
+ case 'cpu':
+ return overview
+ ? ['cpuUsage', 'normalizedLoad1m']
+ : ['cpuUsage', 'cpuUsageBreakdown', 'normalizedLoad1m', 'loadBreakdown'];
+ case 'memory':
+ return overview ? ['memoryUsage'] : ['memoryUsage', 'memoryUsageBreakdown'];
+ case 'network':
+ return ['rxTx'];
+ case 'disk':
+ return overview
+ ? ['diskUsageByMountPoint', 'diskIOReadWrite']
+ : ['diskUsageByMountPoint', 'diskIOReadWrite', 'diskThroughputReadWrite'];
+ case 'log':
+ return ['logRate'];
+ default:
+ return [];
+ }
+};
+
+describe('useHostCharts', () => {
+ describe.each<[HostMetricTypes]>([['cpu'], ['memory'], ['network'], ['disk'], ['log']])(
+ '%s',
+ (item) => {
+ test.each<[HostMetricTypes]>([[item]])(
+ 'should return an array of charts with correct order for metric "%s"',
+ async (metric) => {
+ const expectedOrder = getHostChartsExpectedOrder(metric, false);
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useHostCharts({ dataViewId, metric })
+ );
+ await waitForNextUpdate();
+
+ const { charts } = result.current;
+
+ expect(charts).toHaveLength(expectedOrder.length);
+
+ charts.forEach((chart, index) => {
+ expect(chart).toHaveProperty('id', expectedOrder[index]);
+ });
+ }
+ );
+
+ test.each<[HostMetricTypes]>([[item]])(
+ 'should return an array of charts with correct order for metric "%s" - overview',
+ async (metric) => {
+ const expectedOrder = getHostChartsExpectedOrder(metric, true);
+
+ const { result, waitForNextUpdate } = renderHook(() =>
+ useHostCharts({ dataViewId, metric, options: { overview: true } })
+ );
+ await waitForNextUpdate();
+
+ const { charts } = result.current;
+
+ expect(charts).toHaveLength(expectedOrder.length);
+
+ charts.forEach((chart, index) => {
+ expect(chart).toHaveProperty('id', expectedOrder[index]);
+ });
+ }
+ );
+ }
+ );
});
-describe('useHostPageViewMetricsCharts', () => {
- it('should return an array of charts with correct order', async () => {
+describe('useKubernetesCharts', () => {
+ it('should return an array of charts with correct order - overview', async () => {
const { result, waitForNextUpdate } = renderHook(() =>
- useHostPageViewMetricsCharts({ metricsDataViewId, logsDataViewId })
+ useKubernetesCharts({ dataViewId, options: { overview: true } })
);
await waitForNextUpdate();
- const expectedOrder = [
- 'cpuUsage',
- 'cpuUsageBreakdown',
- 'memoryUsage',
- 'memoryUsageBreakdown',
- 'normalizedLoad1m',
- 'loadBreakdown',
- 'logRate',
- 'diskUsageByMountPoint',
- 'diskThroughputReadWrite',
- 'diskIOReadWrite',
- 'rxTx',
- ];
+ const expectedOrder = ['nodeCpuCapacity', 'nodeMemoryCapacity'];
- expect(result.current).toHaveLength(expectedOrder.length);
+ const { charts } = result.current;
- result.current.forEach((chart, index) => {
+ expect(charts).toHaveLength(expectedOrder.length);
+
+ charts.forEach((chart, index) => {
expect(chart).toHaveProperty('id', expectedOrder[index]);
});
});
- it('should return a chart with id "logRate" using the logsDataViewId', async () => {
- const { result, waitForNextUpdate } = renderHook(() =>
- useHostPageViewMetricsCharts({ metricsDataViewId, logsDataViewId })
- );
- await waitForNextUpdate();
-
- const logRateChart = result.current.find((chart) => chart.id === 'logRate') as LensXYConfig;
- expect(logRateChart).toBeDefined();
- expect(logRateChart.dataset).toHaveProperty('index', logsDataViewId);
- });
-});
-
-describe('useKubernetesSectionMetricsCharts', () => {
it('should return an array of charts with correct order', async () => {
- const { result, waitForNextUpdate } = renderHook(() =>
- useKubernetesSectionMetricsCharts({ metricsDataViewId })
- );
+ const { result, waitForNextUpdate } = renderHook(() => useKubernetesCharts({ dataViewId }));
await waitForNextUpdate();
const expectedOrder = [
@@ -108,9 +111,11 @@ describe('useKubernetesSectionMetricsCharts', () => {
'nodePodCapacity',
];
- expect(result.current).toHaveLength(expectedOrder.length);
+ const { charts } = result.current;
- result.current.forEach((chart, index) => {
+ expect(charts).toHaveLength(expectedOrder.length);
+
+ charts.forEach((chart, index) => {
expect(chart).toHaveProperty('id', expectedOrder[index]);
});
});
@@ -118,9 +123,7 @@ describe('useKubernetesSectionMetricsCharts', () => {
describe('useHostKpiCharts', () => {
it('should return an array of charts with correct order', async () => {
- const { result, waitForNextUpdate } = renderHook(() =>
- useHostKpiCharts({ dataViewId: metricsDataViewId })
- );
+ const { result, waitForNextUpdate } = renderHook(() => useHostKpiCharts({ dataViewId }));
await waitForNextUpdate();
const expectedOrder = ['cpuUsage', 'normalizedLoad1m', 'memoryUsage', 'diskUsage'];
@@ -142,7 +145,7 @@ describe('useHostKpiCharts', () => {
};
const { result, waitForNextUpdate } = renderHook(() =>
- useHostKpiCharts({ dataViewId: metricsDataViewId, options })
+ useHostKpiCharts({ dataViewId, options })
);
await waitForNextUpdate();
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts
index b91a819542b24..5450e86029670 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_metrics_charts.ts
@@ -9,111 +9,69 @@ import { i18n } from '@kbn/i18n';
import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common';
import useAsync from 'react-use/lib/useAsync';
-export const useHostFlyoutViewMetricsCharts = ({
- metricsDataViewId,
- logsDataViewId,
-}: {
- metricsDataViewId?: string;
- logsDataViewId?: string;
-}) => {
- const model = findInventoryModel('host');
-
- const { value: charts = [] } = useAsync(async () => {
- const { cpu, disk, memory, network, logs } = await model.metrics.getCharts();
-
- return [
- cpu.xy.cpuUsage,
- memory.xy.memoryUsage,
- cpu.xy.normalizedLoad1m,
- logs.xy.logRate,
- disk.xy.diskUsageByMountPoint,
- disk.xy.diskThroughputReadWrite,
- disk.xy.diskIOReadWrite,
- network.xy.rxTx,
- ].map((chart) => {
- const dataViewId = chart.id === 'logRate' ? logsDataViewId : metricsDataViewId;
- return {
- ...chart,
- ...(dataViewId && {
- dataset: {
- index: dataViewId,
- },
- }),
- };
- });
- }, [metricsDataViewId, logsDataViewId]);
-
- return charts;
-};
+export type HostMetricTypes = 'cpu' | 'memory' | 'network' | 'disk' | 'log' | 'kpi';
+interface UseChartsOptions {
+ overview?: boolean;
+}
-export const useHostPageViewMetricsCharts = ({
- metricsDataViewId,
- logsDataViewId,
+export const useHostCharts = ({
+ metric,
+ dataViewId,
+ options,
}: {
- metricsDataViewId?: string;
- logsDataViewId?: string;
+ metric: HostMetricTypes;
+ dataViewId?: string;
+ options?: UseChartsOptions;
}) => {
- const model = findInventoryModel('host');
-
- const { value: charts = [] } = useAsync(async () => {
- const { cpu, disk, memory, network, logs } = await model.metrics.getCharts();
-
- return [
- cpu.xy.cpuUsage,
- cpu.xy.cpuUsageBreakdown,
- memory.xy.memoryUsage,
- memory.xy.memoryUsageBreakdown,
- cpu.xy.normalizedLoad1m,
- cpu.xy.loadBreakdown,
- logs.xy.logRate,
- disk.xy.diskUsageByMountPoint,
- disk.xy.diskThroughputReadWrite,
- disk.xy.diskIOReadWrite,
- network.xy.rxTx,
- ].map((chart) => {
- const dataViewId = chart.id === 'logRate' ? logsDataViewId : metricsDataViewId;
- return {
- ...chart,
- ...(dataViewId && {
- dataset: {
- index: dataViewId,
- },
- }),
- };
- });
- }, [metricsDataViewId, logsDataViewId]);
+ const { value: charts = [], error } = useAsync(async () => {
+ const hostCharts = await getHostsCharts({ metric, options });
+ return hostCharts.map((chart) => ({
+ ...chart,
+ ...(dataViewId && {
+ dataset: {
+ index: dataViewId,
+ },
+ }),
+ }));
+ }, [dataViewId]);
- return charts;
+ return { charts, error };
};
-export const useKubernetesSectionMetricsCharts = ({
- metricsDataViewId,
+export const useKubernetesCharts = ({
+ dataViewId,
+ options,
}: {
- metricsDataViewId?: string;
+ dataViewId?: string;
+ options?: UseChartsOptions;
}) => {
const model = findInventoryModel('host');
- const { value: charts = [] } = useAsync(async () => {
+ const { value: charts = [], error } = useAsync(async () => {
const { kibernetesNode } = await model.metrics.getCharts();
- return [
- kibernetesNode.xy.nodeCpuCapacity,
- kibernetesNode.xy.nodeMemoryCapacity,
- kibernetesNode.xy.nodeDiskCapacity,
- kibernetesNode.xy.nodePodCapacity,
- ].map((chart) => {
+ const items = options?.overview
+ ? [kibernetesNode.xy.nodeCpuCapacity, kibernetesNode.xy.nodeMemoryCapacity]
+ : [
+ kibernetesNode.xy.nodeCpuCapacity,
+ kibernetesNode.xy.nodeMemoryCapacity,
+ kibernetesNode.xy.nodeDiskCapacity,
+ kibernetesNode.xy.nodePodCapacity,
+ ];
+
+ return items.map((chart) => {
return {
...chart,
- ...(metricsDataViewId && {
+ ...(dataViewId && {
dataset: {
- index: metricsDataViewId,
+ index: dataViewId,
},
}),
};
});
- }, [metricsDataViewId]);
+ }, [dataViewId, options?.overview]);
- return charts;
+ return { charts, error };
};
const getSubtitleFromFormula = (value: string) =>
@@ -132,7 +90,7 @@ export const useHostKpiCharts = ({
}) => {
const { value: charts = [] } = useAsync(async () => {
const model = findInventoryModel('host');
- const { cpu, disk, memory } = await model.metrics.getCharts();
+ const { cpu, memory, disk } = await model.metrics.getCharts();
return [
cpu.metric.cpuUsage,
@@ -156,3 +114,40 @@ export const useHostKpiCharts = ({
return charts;
};
+
+const getHostsCharts = async ({
+ metric,
+ options,
+}: {
+ metric: HostMetricTypes;
+ options?: UseChartsOptions;
+}) => {
+ const model = findInventoryModel('host');
+ const { cpu, memory, network, disk, logs } = await model.metrics.getCharts();
+
+ switch (metric) {
+ case 'cpu':
+ return options?.overview
+ ? [cpu.xy.cpuUsage, cpu.xy.normalizedLoad1m]
+ : [
+ cpu.xy.cpuUsage,
+ cpu.xy.cpuUsageBreakdown,
+ cpu.xy.normalizedLoad1m,
+ cpu.xy.loadBreakdown,
+ ];
+ case 'memory':
+ return options?.overview
+ ? [memory.xy.memoryUsage]
+ : [memory.xy.memoryUsage, memory.xy.memoryUsageBreakdown];
+ case 'network':
+ return [network.xy.rxTx];
+ case 'disk':
+ return options?.overview
+ ? [disk.xy.diskUsageByMountPoint, disk.xy.diskIOReadWrite]
+ : [disk.xy.diskUsageByMountPoint, disk.xy.diskIOReadWrite, disk.xy.diskThroughputReadWrite];
+ case 'log':
+ return [logs.xy.logRate];
+ default:
+ return [];
+ }
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_tab_switcher.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
index 9ab02ce3f5fd6..a14b71f009efa 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/hooks/use_tab_switcher.tsx
@@ -8,7 +8,7 @@
import createContainer from 'constate';
import { useLazyRef } from '../../../hooks/use_lazy_ref';
import type { TabIds } from '../types';
-import { AssetDetailsUrlState, useAssetDetailsUrlState } from './use_asset_details_url_state';
+import { useAssetDetailsUrlState } from './use_asset_details_url_state';
interface TabSwitcherParams {
defaultActiveTabId?: TabIds;
@@ -16,23 +16,31 @@ interface TabSwitcherParams {
export function useTabSwitcher({ defaultActiveTabId }: TabSwitcherParams) {
const [urlState, setUrlState] = useAssetDetailsUrlState();
- const activeTabId: TabIds | undefined = urlState?.tabId || defaultActiveTabId;
+ const [activeTabId = defaultActiveTabId, scrollTo] = urlState?.tabId?.split('#') ?? [];
// This set keeps track of which tabs content have been rendered the first time.
// We need it in order to load a tab content only if it gets clicked, and then keep it in the DOM for performance improvement.
const renderedTabsSet = useLazyRef(() => new Set([activeTabId]));
- const showTab = (tabId: TabIds) => {
+ const showTab = (tabId: TabIds, options?: { scrollTo?: string }) => {
// On a tab click, mark the tab content as allowed to be rendered
renderedTabsSet.current.add(tabId);
- setUrlState({ tabId: tabId as AssetDetailsUrlState['tabId'] });
+ setUrlState({
+ tabId: options?.scrollTo ? `${tabId}#${options?.scrollTo}` : tabId,
+ });
+ };
+
+ const setScrollTo = (to: string) => {
+ showTab(activeTabId as TabIds, { scrollTo: to });
};
return {
activeTabId,
+ scrollTo,
renderedTabsSet,
showTab,
+ setScrollTo,
};
}
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_alerts_page.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_alerts_page.tsx
index e23fa128868b0..2b5f80043590e 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_alerts_page.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_alerts_page.tsx
@@ -13,18 +13,18 @@ import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
import { ALERTS_PATH } from '../../shared/alerts/constants';
export interface LinkToAlertsPageProps {
- assetName: string;
+ assetId: string;
dateRange: TimeRange;
queryField: string;
}
-export const LinkToAlertsPage = ({ assetName, queryField, dateRange }: LinkToAlertsPageProps) => {
+export const LinkToAlertsPage = ({ assetId, queryField, dateRange }: LinkToAlertsPageProps) => {
const { services } = useKibanaContextForPlugin();
const { http } = services;
const linkToAlertsPage = http.basePath.prepend(
`${ALERTS_PATH}?_a=${encode({
- kuery: `${queryField}:"${assetName}"`,
+ kuery: `${queryField}:"${assetId}"`,
rangeFrom: dateRange.from,
rangeTo: dateRange.to,
status: 'all',
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_apm_services.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_apm_services.tsx
index 7be6b16d5e5a3..0c8013cbd655a 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_apm_services.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/links/link_to_apm_services.tsx
@@ -12,18 +12,18 @@ import { EuiButtonEmpty } from '@elastic/eui';
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
export interface LinkToApmServicesProps {
- assetName: string;
+ assetId: string;
apmField: string;
}
-export const LinkToApmServices = ({ assetName, apmField }: LinkToApmServicesProps) => {
+export const LinkToApmServices = ({ assetId, apmField }: LinkToApmServicesProps) => {
const { services } = useKibanaContextForPlugin();
const { http } = services;
const queryString = new URLSearchParams(
encode(
stringify({
- kuery: `${apmField}:"${assetName}"`,
+ kuery: `${apmField}:"${assetId}"`,
})
)
);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/common/popover.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/common/popover.tsx
index 43fb037eacd18..2e84514829554 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/common/popover.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/common/popover.tsx
@@ -6,6 +6,7 @@
*/
import { EuiPopover, EuiIcon, type IconType, type IconColor, type IconSize } from '@elastic/eui';
+import { css } from '@emotion/react';
import React from 'react';
import { useBoolean } from '../../../../hooks/use_boolean';
@@ -23,15 +24,22 @@ export const Popover = ({
'data-test-subj'?: string;
}) => {
const [isPopoverOpen, { off: closePopover, toggle: togglePopover }] = useBoolean(false);
+
return (
{
e.stopPropagation();
togglePopover();
}}
+ css={css`
+ display: flex;
+ `}
data-test-subj={props['data-test-subj']}
>
{
}, [nodeLogsLocator, asset.name, asset.type, state.startTimestamp, textQueryDebounced, logView]);
return (
-
+
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx
new file mode 100644
index 0000000000000..3246d452399cb
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/host_metrics.tsx
@@ -0,0 +1,51 @@
+/*
+ * 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, { useRef } from 'react';
+import { HostMetricTypes } from '../../hooks/use_metrics_charts';
+import { useDatePickerContext } from '../../hooks/use_date_picker';
+import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
+import { useDataViewsContext } from '../../hooks/use_data_views';
+import { useIntersectingState } from '../../hooks/use_intersecting_state';
+import { MetricsTemplate } from './metrics_template';
+import { HostCharts, KubernetesCharts } from '../../charts';
+
+const METRIC_TYPES: Array> = [
+ 'cpu',
+ 'memory',
+ 'network',
+ 'disk',
+ 'log',
+];
+
+export const HostMetrics = () => {
+ const ref = useRef(null);
+ const { dateRange } = useDatePickerContext();
+ const { asset } = useAssetDetailsRenderPropsContext();
+ const { metrics, logs } = useDataViewsContext();
+
+ const state = useIntersectingState(ref, { dateRange });
+
+ return (
+
+ {METRIC_TYPES.map((metric) => (
+
+ ))}
+
+
+ );
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics.tsx
new file mode 100644
index 0000000000000..6ac49ba9abfe5
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics.tsx
@@ -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 React from 'react';
+import { HostMetrics } from './host_metrics';
+import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
+
+export const Metrics = () => {
+ const { asset } = useAssetDetailsRenderPropsContext();
+
+ switch (asset.type) {
+ case 'host':
+ return ;
+ default:
+ return null;
+ }
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics_template.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics_template.tsx
new file mode 100644
index 0000000000000..e170f4a819081
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/metrics/metrics_template.tsx
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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, { useRef, useCallback, useEffect } from 'react';
+import {
+ EuiFlexItem,
+ EuiFlexGroup,
+ useEuiTheme,
+ useEuiMaxBreakpoint,
+ useEuiMinBreakpoint,
+ useIsWithinBreakpoints,
+ useResizeObserver,
+ EuiListGroup,
+ EuiListGroupItem,
+} from '@elastic/eui';
+import { css, cx } from '@emotion/css';
+import { useKibanaHeader } from '../../../../hooks/use_kibana_header';
+import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
+import { useTabSwitcherContext } from '../../hooks/use_tab_switcher';
+
+export const MetricsTemplate = React.forwardRef(
+ ({ children }, ref) => {
+ const { actionMenuHeight } = useKibanaHeader();
+ const { euiTheme } = useEuiTheme();
+ const { renderMode } = useAssetDetailsRenderPropsContext();
+ const { scrollTo, setScrollTo } = useTabSwitcherContext();
+
+ const scrollTimeoutRef = useRef(null);
+ const initialScrollTimeoutRef = useRef(null);
+
+ const quickAccessItemsRef = useRef>(new Map());
+ const contentRefs = useRef<{ [key: string]: HTMLDivElement | null }>({});
+ const quickAccessRef = useRef(null);
+
+ const isLargeScreen = useIsWithinBreakpoints(['xl'], true);
+ const listGroupDimensions = useResizeObserver(quickAccessRef.current);
+
+ const kibanaHeaderOffset =
+ renderMode.mode === 'flyout'
+ ? `0px`
+ : `calc(${actionMenuHeight}px + var(--euiFixedHeadersOffset, 0))`;
+
+ const quickAccessHorizontalOffset = isLargeScreen
+ ? `${euiTheme.size.s} - 1px` // arbitrary value to align with the content
+ : `${listGroupDimensions.height}px`;
+
+ const quickAccessOffset = `calc(${kibanaHeaderOffset} + ${quickAccessHorizontalOffset})`;
+
+ const setContentRef = useCallback((contentRef: HTMLDivElement | null) => {
+ const sectionId = contentRef?.getAttribute('data-section-id');
+ const label = contentRef?.innerText;
+ if (!sectionId) {
+ return;
+ }
+
+ contentRefs.current[sectionId] = contentRef;
+
+ if (!quickAccessItemsRef.current.has(sectionId)) {
+ quickAccessItemsRef.current.set(sectionId, label);
+ }
+ }, []);
+
+ const scrollToSection = useCallback((sectionId: string, retries = 5, delay = 300) => {
+ const chartEl = contentRefs.current[sectionId]?.querySelectorAll('.euiPanel') ?? [];
+ if (chartEl.length > 0) {
+ contentRefs.current[sectionId]?.scrollIntoView({
+ behavior: 'smooth',
+ block: 'start',
+ });
+ } else if (retries > 0) {
+ // Retry scrolling after a delay - needed for conditionally rendered content
+ const nextDelay = delay * 2;
+ scrollTimeoutRef.current = setTimeout(
+ () => scrollToSection(sectionId, retries - 1),
+ nextDelay
+ );
+ }
+ }, []);
+
+ const onQuickAccessItemClick = (metric: string) => {
+ if (metric !== scrollTo) {
+ setScrollTo(metric);
+ } else {
+ scrollToSection(metric);
+ }
+ };
+
+ useEffect(() => {
+ if (scrollTo) {
+ // Wait for the calculation of quickAccessOffset
+ initialScrollTimeoutRef.current = setTimeout(() => scrollToSection(scrollTo), 100);
+ }
+ }, [scrollTo, scrollToSection]);
+
+ useEffect(
+ () => () => {
+ [scrollTimeoutRef.current, initialScrollTimeoutRef.current]
+ .filter((timeout): timeout is NodeJS.Timeout => !!timeout)
+ .forEach((timeout) => clearTimeout(timeout));
+ },
+ []
+ );
+
+ const quickAccessItems = [...quickAccessItemsRef.current];
+
+ return (
+
+
+
+
+ {quickAccessItems.map(([sectionId, label]) => (
+ onQuickAccessItemClick(sectionId)}
+ color="text"
+ size="s"
+ className={cx({
+ [css`
+ text-decoration: underline;
+ `]: sectionId === scrollTo,
+ })}
+ css={css`
+ background-color: unset;
+ & > button {
+ padding-block: ${euiTheme.size.s};
+ padding-inline: 0px;
+ }
+ &:hover,
+ &:focus-within {
+ background-color: unset;
+ }
+ `}
+ label={label}
+ />
+ ))}
+
+
+
+
+ [data-section-id] {
+ scroll-margin-top: ${quickAccessOffset};
+ }
+ `}
+ >
+ {React.Children.map(children, (child, index) => {
+ if (React.isValidElement(child)) {
+ return React.cloneElement(child as React.ReactElement, {
+ ref: setContentRef,
+ key: index,
+ });
+ }
+ })}
+
+
+
+ );
+ }
+);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts/alerts.tsx
similarity index 68%
rename from x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts.tsx
rename to x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts/alerts.tsx
index c1330f431ecc9..60f09b1d73b91 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/alerts/alerts.tsx
@@ -10,24 +10,24 @@ import { EuiFlexGroup, EuiFlexItem, type EuiAccordionProps } from '@elastic/eui'
import type { TimeRange } from '@kbn/es-query';
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
-import { usePluginConfig } from '../../../../containers/plugin_config_context';
-import { LinkToAlertsRule } from '../../links/link_to_alerts';
-import { LinkToAlertsPage } from '../../links/link_to_alerts_page';
-import { AlertFlyout } from '../../../../alerting/inventory/components/alert_flyout';
-import { useBoolean } from '../../../../hooks/use_boolean';
-import { AlertsSectionTitle } from '../../components/section_titles';
-import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
-import { CollapsibleSection } from './section/collapsible_section';
+import { usePluginConfig } from '../../../../../containers/plugin_config_context';
+import { LinkToAlertsRule } from '../../../links/link_to_alerts';
+import { LinkToAlertsPage } from '../../../links/link_to_alerts_page';
+import { AlertFlyout } from '../../../../../alerting/inventory/components/alert_flyout';
+import { useBoolean } from '../../../../../hooks/use_boolean';
+import { AlertsSectionTitle } from '../section_titles';
+import { useAssetDetailsRenderPropsContext } from '../../../hooks/use_asset_details_render_props';
+import { Section } from '../../../components/section';
import { AlertsClosedContent } from './alerts_closed_content';
-import { type AlertsCount } from '../../../../hooks/use_alerts_count';
-import { AlertsOverview } from '../../../shared/alerts/alerts_overview';
+import { type AlertsCount } from '../../../../../hooks/use_alerts_count';
+import { AlertsOverview } from '../../../../shared/alerts/alerts_overview';
export const AlertsSummaryContent = ({
- assetName,
+ assetId,
assetType,
dateRange,
}: {
- assetName: string;
+ assetId: string;
assetType: InventoryItemType;
dateRange: TimeRange;
}) => {
@@ -46,10 +46,12 @@ export const AlertsSummaryContent = ({
setActiveAlertsCount(alertsCount?.activeAlertCount);
};
+ const assetIdField = findInventoryFields(assetType).id;
+
return (
<>
- }
collapsible
data-test-subj="infraAssetDetailsAlertsCollapsible"
id="alerts"
@@ -63,20 +65,16 @@ export const AlertsSummaryContent = ({
)}
-
+
}
>
-
-
+
+
{featureFlags.inventoryThresholdAlertRuleEnabled && (
{
+export const KPIGrid = ({ assetId, dataView, dateRange }: Props) => {
const { searchSessionId } = useLoadingStateContext();
const filters = useMemo(() => {
return [
buildCombinedHostsFilter({
- field: 'host.name',
- values: [assetName],
+ field: findInventoryFields('host').id,
+ values: [assetId],
dataView,
}),
];
- }, [dataView, assetName]);
+ }, [dataView, assetId]);
return (
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
index 96c19cffcec56..b41223c7185fc 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metadata_summary/metadata_summary_list.tsx
@@ -23,8 +23,8 @@ import { ContentTabIds } from '../../../types';
import { ExpandableContent } from '../../../components/expandable_content';
import { MetadataHeader } from './metadata_header';
import { MetadataExplanationMessage } from '../../../components/metadata_explanation';
-import { MetadataSectionTitle } from '../../../components/section_titles';
-import { CollapsibleSection } from '../section/collapsible_section';
+import { SectionTitle } from '../../../components/section_title';
+import { Section } from '../../../components/section';
interface MetadataSummaryProps {
metadata: InfraMetadata | null;
@@ -81,53 +81,61 @@ const MetadataSummaryListWrapper = ({
};
return (
-
-
+
-
- }
- >
- <>
-
-
-
- {visibleMetadata
- .filter((metadataValue) => metadataValue)
- .map((metadataValue) => (
-
-
-
-
- {metadataLoading && !metadataValue.value ? (
-
- ) : (
-
- )}
-
-
-
- ))}
-
- >
-
-
+ }
+ data-test-subj="infraAssetDetailsMetadataTitle"
+ />
+ }
+ collapsible
+ data-test-subj="infraAssetDetailsMetadataCollapsible"
+ id="metadata"
+ extraAction={
+
+
+
+ }
+ >
+ <>
+
+
+
+ {visibleMetadata
+ .filter((metadataValue) => metadataValue)
+ .map((metadataValue) => (
+
+
+
+
+ {metadataLoading && !metadataValue.value ? (
+
+ ) : (
+
+ )}
+
+
+
+ ))}
+
+ >
+
);
};
export const MetadataSummaryList = ({ metadata, loading }: MetadataSummaryProps) => (
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx
new file mode 100644
index 0000000000000..e7fe8bf795b47
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/host_metrics.tsx
@@ -0,0 +1,39 @@
+/*
+ * 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 { EuiFlexGroup, EuiFlexGrid } from '@elastic/eui';
+import type { TimeRange } from '@kbn/es-query';
+import type { DataView } from '@kbn/data-views-plugin/public';
+import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher';
+import { HostCharts, KubernetesCharts } from '../../../charts';
+import { ContentTabIds } from '../../../types';
+
+interface Props {
+ assetId: string;
+ dateRange: TimeRange;
+ dataView?: DataView;
+}
+
+export const HostMetrics = (props: Props) => {
+ const { showTab } = useTabSwitcherContext();
+
+ const onClick = (metric: string) => {
+ showTab(ContentTabIds.METRICS, { scrollTo: metric });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.tsx
new file mode 100644
index 0000000000000..b5cdac6bd4a74
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics.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
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import React, { useMemo } from 'react';
+import type { TimeRange } from '@kbn/es-query';
+import type { DataView } from '@kbn/data-views-plugin/public';
+import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
+import { HostMetrics } from './host_metrics';
+import { Section } from '../../../components/section';
+import { MetricsSectionTitle } from '../section_titles';
+
+interface Props {
+ assetId: string;
+ assetType: InventoryItemType;
+ dateRange: TimeRange;
+ dataView?: DataView;
+}
+
+export const MetricsContent = ({ assetType, ...props }: Props) => {
+ const content = useMemo(() => {
+ switch (assetType) {
+ case 'host':
+ return ;
+ default:
+ return null;
+ }
+ }, [assetType, props]);
+
+ return (
+ }
+ data-test-subj="infraAssetDetailsMetricsCollapsible"
+ id="metrics"
+ collapsible
+ >
+ {content}
+
+ );
+};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx
deleted file mode 100644
index 4477c5d12907f..0000000000000
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_grid.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import React from 'react';
-import { EuiFlexItem, EuiFlexGrid } from '@elastic/eui';
-import type { TimeRange } from '@kbn/es-query';
-import type { LensConfig } from '@kbn/lens-embeddable-utils/config_builder';
-import { Chart } from './chart';
-
-interface Props {
- assetName: string;
- dateRange: TimeRange;
- filterFieldName: string;
- charts: Array;
- ['data-test-subj']: string;
-}
-
-export const MetricsGrid = ({ assetName, dateRange, filterFieldName, charts, ...props }: Props) => {
- return (
-
- {charts.map((chartProp, index) => (
-
-
-
- ))}
-
- );
-};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx
deleted file mode 100644
index 72e414a5c266c..0000000000000
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/metrics/metrics_section.tsx
+++ /dev/null
@@ -1,138 +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, { useMemo } from 'react';
-
-import type { DataView } from '@kbn/data-views-plugin/public';
-import type { TimeRange } from '@kbn/es-query';
-import { EuiFlexGroup } from '@elastic/eui';
-import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common';
-import {
- MetricsSectionTitle,
- KubernetesMetricsSectionTitle,
-} from '../../../components/section_titles';
-import { useMetadataStateContext } from '../../../hooks/use_metadata_state';
-import { MetricsGrid } from './metrics_grid';
-import { CollapsibleSection } from '../section/collapsible_section';
-import {
- useHostFlyoutViewMetricsCharts,
- useHostPageViewMetricsCharts,
- useKubernetesSectionMetricsCharts,
-} from '../../../hooks/use_metrics_charts';
-
-interface Props {
- assetName: string;
- dateRange: TimeRange;
- metricsDataView?: DataView;
- logsDataView?: DataView;
-}
-
-export const MetricsSection = (props: Props) => {
- return (
-
-
-
-
- );
-};
-
-export const MetricsSectionCompact = ({
- assetName,
- metricsDataView,
- logsDataView,
- dateRange,
-}: Props) => {
- const model = findInventoryModel('host');
- const charts = useHostFlyoutViewMetricsCharts({
- metricsDataViewId: metricsDataView?.id,
- logsDataViewId: logsDataView?.id,
- });
-
- return (
-
- );
-};
-
-const HostMetricsSection = ({ assetName, metricsDataView, logsDataView, dateRange }: Props) => {
- const model = findInventoryModel('host');
- const charts = useHostPageViewMetricsCharts({
- metricsDataViewId: metricsDataView?.id,
- logsDataViewId: logsDataView?.id,
- });
-
- return (
-
- );
-};
-
-const KubenetesMetricsSection = ({
- assetName,
- metricsDataView,
- dateRange,
-}: Omit) => {
- const model = findInventoryModel('host');
- const charts = useKubernetesSectionMetricsCharts({ metricsDataViewId: metricsDataView?.id });
-
- return (
-
- );
-};
-
-const Section = ({
- title,
- dependsOn = [],
- collapsible = false,
- children,
-}: {
- title: React.FunctionComponent;
- dependsOn?: string[];
- collapsible?: boolean;
- children: React.ReactNode;
-}) => {
- const { metadata } = useMetadataStateContext();
-
- const shouldRender = useMemo(
- () =>
- dependsOn.length === 0 ||
- dependsOn.some((p) => (metadata?.features ?? []).some((f) => f.name === p)),
- [dependsOn, metadata?.features]
- );
-
- return shouldRender ? (
-
- {children}
-
- ) : null;
-};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx
index 793276eaf153f..c67347a765d69 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/overview.tsx
@@ -6,23 +6,23 @@
*/
import React, { useRef } from 'react';
-import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
+import { css } from '@emotion/react';
import {
MetadataSummaryList,
MetadataSummaryListCompact,
} from './metadata_summary/metadata_summary_list';
-import { AlertsSummaryContent } from './alerts';
+import { AlertsSummaryContent } from './alerts/alerts';
import { KPIGrid } from './kpis/kpi_grid';
-import { MetricsSection, MetricsSectionCompact } from './metrics/metrics_section';
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
import { useMetadataStateContext } from '../../hooks/use_metadata_state';
import { useDataViewsContext } from '../../hooks/use_data_views';
import { useDatePickerContext } from '../../hooks/use_date_picker';
-import { SectionSeparator } from './section_separator';
import { MetadataErrorCallout } from '../../components/metadata_error_callout';
import { useIntersectingState } from '../../hooks/use_intersecting_state';
import { CpuProfilingPrompt } from './kpis/cpu_profiling_prompt';
import { ServicesContent } from './services';
+import { MetricsContent } from './metrics/metrics';
export const Overview = () => {
const ref = useRef(null);
@@ -33,27 +33,12 @@ export const Overview = () => {
loading: metadataLoading,
error: fetchMetadataError,
} = useMetadataStateContext();
- const { logs, metrics } = useDataViewsContext();
+ const { metrics } = useDataViewsContext();
const isFullPageView = renderMode.mode === 'page';
const state = useIntersectingState(ref, { dateRange });
- const metricsSection = isFullPageView ? (
-
- ) : (
-
- );
const metadataSummarySection = isFullPageView ? (
) : (
@@ -63,7 +48,7 @@ export const Overview = () => {
return (
-
+
@@ -72,7 +57,7 @@ export const Overview = () => {
@@ -80,11 +65,27 @@ export const Overview = () => {
{asset.type === 'host' ? (
-
+
) : null}
- {metricsSection}
+
+
+
);
};
+
+const SectionSeparator = () => (
+
+);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section/collapsible_section.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section/collapsible_section.tsx
deleted file mode 100644
index da0b993199ee1..0000000000000
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section/collapsible_section.tsx
+++ /dev/null
@@ -1,91 +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, { useEffect, useState } from 'react';
-
-import {
- EuiAccordion,
- EuiFlexGroup,
- EuiFlexItem,
- useGeneratedHtmlId,
- type EuiAccordionProps,
-} from '@elastic/eui';
-
-export const CollapsibleSection = ({
- title,
- closedSectionContent,
- extraAction,
- children,
- collapsible,
- ['data-test-subj']: dataTestSubj,
- id,
- initialTriggerValue,
-}: {
- title: React.FunctionComponent;
- closedSectionContent?: React.ReactNode;
- extraAction?: React.ReactNode;
- dependsOn?: string[];
- children: React.ReactNode;
- collapsible: boolean;
- ['data-test-subj']: string;
- id: string;
- initialTriggerValue?: EuiAccordionProps['forceState'];
-}) => {
- const [trigger, setTrigger] = useState('open');
-
- useEffect(() => {
- setTrigger(initialTriggerValue ?? 'open');
- }, [initialTriggerValue]);
-
- const Title = title;
- const ButtonContent = () =>
- closedSectionContent && trigger === 'closed' ? (
-
-
-
-
- {closedSectionContent}
-
- ) : (
-
- );
- const collapsibleSectionAccordionId = useGeneratedHtmlId({
- prefix: id,
- });
-
- const onToggle = (isOpen: boolean) => {
- const newState = isOpen ? 'open' : 'closed';
- setTrigger(newState);
- };
-
- return collapsible ? (
- }
- buttonProps={{ 'data-test-subj': dataTestSubj }}
- paddingSize="s"
- initialIsOpen={true}
- extraAction={extraAction ?? undefined}
- forceState={trigger}
- onToggle={onToggle}
- data-section-state={trigger}
- data-test-subj="infraAssetDetailsCollapseExpandSection"
- >
- {children}
-
- ) : (
-
-
-
-
- {children}
-
- );
-};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_separator.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_separator.tsx
deleted file mode 100644
index 01f571bfc148a..0000000000000
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_separator.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { EuiHorizontalRule } from '@elastic/eui';
-import { css } from '@emotion/react';
-
-export const SectionSeparator = () => (
-
-);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.tsx
new file mode 100644
index 0000000000000..c330944ffbcdc
--- /dev/null
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/section_titles.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 React from 'react';
+import { i18n } from '@kbn/i18n';
+import { HostMetricsExplanationContent } from '../../../lens';
+import { TitleWithTooltip } from '../../components/section_title';
+import { AlertsTooltipContent } from '../../components/alerts_tooltip_content';
+import { ServicesTooltipContent } from '../../components/services_tooltip_content';
+
+export const MetricsSectionTitle = () => {
+ return (
+ }
+ />
+ );
+};
+
+export const AlertsSectionTitle = () => {
+ return (
+ }
+ />
+ );
+};
+
+export const ServicesSectionTitle = () => (
+ }
+ />
+);
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx
index 9f6e420758c2c..edd6bebb4e96d 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/tabs/overview/services.tsx
@@ -10,8 +10,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiCallOut, EuiLink } fro
import { FormattedMessage } from '@kbn/i18n-react';
import type { TimeRange } from '@kbn/es-query';
import { useLinkProps } from '@kbn/observability-shared-plugin/public';
-import { CollapsibleSection } from './section/collapsible_section';
-import { ServicesSectionTitle } from '../../components/section_titles';
+import { Section } from '../../components/section';
+import { ServicesSectionTitle } from './section_titles';
import { useServices } from '../../hooks/use_services';
import { HOST_FIELD } from '../../../../../common/constants';
import { LinkToApmServices } from '../../links';
@@ -48,12 +48,12 @@ export const ServicesContent = ({
const hasServices = services?.length;
return (
- }
collapsible
data-test-subj="infraAssetDetailsServicesCollapsible"
id="services"
- extraAction={ }
+ extraAction={ }
>
{error ? (
)}
-
+
);
};
diff --git a/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts b/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts
index e2fe929e6e604..1313e655f6b6f 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts
+++ b/x-pack/plugins/observability_solution/infra/public/components/asset_details/translations.ts
@@ -10,3 +10,24 @@ import { i18n } from '@kbn/i18n';
export const NOT_AVAILABLE_LABEL = i18n.translate('xpack.infra.assetDetails.notApplicableLabel', {
defaultMessage: 'N/A',
});
+
+export const HOST_METRIC_GROUP_TITLES = {
+ cpu: i18n.translate('xpack.infra.metricsGroup.cpu', {
+ defaultMessage: 'CPU',
+ }),
+ memory: i18n.translate('xpack.infra.metricsGroup.memory', {
+ defaultMessage: 'Memory',
+ }),
+ network: i18n.translate('xpack.infra.metricsGroup.network', {
+ defaultMessage: 'Network',
+ }),
+ disk: i18n.translate('xpack.infra.metricsGroup.disk', {
+ defaultMessage: 'Disk',
+ }),
+ log: i18n.translate('xpack.infra.metricsGroup.log', {
+ defaultMessage: 'Log Rate',
+ }),
+ kubernetes: i18n.translate('xpack.infra.metricsGroup.kubernetes', {
+ defaultMessage: 'Kubernetes',
+ }),
+};
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 5e0124fa8a69c..0299bd3582c8b 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
@@ -20,6 +20,7 @@ export interface Asset {
export enum ContentTabIds {
OVERVIEW = 'overview',
METADATA = 'metadata',
+ METRICS = 'metrics',
PROCESSES = 'processes',
PROFILING = 'profiling',
ANOMALIES = 'anomalies',
diff --git a/x-pack/plugins/observability_solution/infra/public/components/shared/alerts/alerts_overview.tsx b/x-pack/plugins/observability_solution/infra/public/components/shared/alerts/alerts_overview.tsx
index 66a679ff51d45..d06d2d672fad6 100644
--- a/x-pack/plugins/observability_solution/infra/public/components/shared/alerts/alerts_overview.tsx
+++ b/x-pack/plugins/observability_solution/infra/public/components/shared/alerts/alerts_overview.tsx
@@ -20,7 +20,7 @@ import AlertsStatusFilter from './alerts_status_filter';
import { useAssetDetailsUrlState } from '../../asset_details/hooks/use_asset_details_url_state';
interface AlertsOverviewProps {
- assetName: string;
+ assetId: string;
dateRange: TimeRange;
onLoaded: (alertsCount?: AlertsCount) => void;
onRangeSelection?: HostsStateUpdater;
@@ -29,7 +29,7 @@ interface AlertsOverviewProps {
const alertFeatureIds = [...infraAlertFeatureIds, AlertConsumers.OBSERVABILITY];
export const AlertsOverview = ({
- assetName,
+ assetId,
dateRange,
onLoaded,
onRangeSelection,
@@ -54,20 +54,20 @@ export const AlertsOverview = ({
() =>
createAlertsEsQuery({
dateRange,
- hostNodeNames: [assetName],
+ assetIds: [assetId],
status: alertStatus,
}),
- [assetName, dateRange, alertStatus]
+ [assetId, dateRange, alertStatus]
);
const alertsEsQuery = useMemo(
() =>
createAlertsEsQuery({
dateRange,
- hostNodeNames: [assetName],
+ assetIds: [assetId],
status: ALERT_STATUS_ALL,
}),
- [assetName, dateRange]
+ [assetId, dateRange]
);
const summaryTimeRange = useSummaryTimeRange(dateRange);
diff --git a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts
index f72908e790f7a..2d9204cfba99e 100644
--- a/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts
+++ b/x-pack/plugins/observability_solution/infra/public/pages/metrics/hosts/hooks/use_alerts_query.ts
@@ -18,12 +18,12 @@ export const useAlertsQueryImpl = () => {
const [alertStatus, setAlertStatus] = useState('all');
- const hostNodeNames = useMemo(() => hostNodes.map((n) => n.name), [hostNodes]);
+ const assetIds = useMemo(() => hostNodes.map((n) => n.name), [hostNodes]);
const getAlertsEsQuery = useCallback(
(status?: AlertStatus) =>
- createAlertsEsQuery({ dateRange: searchCriteria.dateRange, hostNodeNames, status }),
- [hostNodeNames, searchCriteria.dateRange]
+ createAlertsEsQuery({ dateRange: searchCriteria.dateRange, assetIds, status }),
+ [assetIds, searchCriteria.dateRange]
);
// Regenerate the query when status change even if is not used.
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 07c4e9e32993a..31b16ca70f26d 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
@@ -28,7 +28,7 @@ const flyoutTabs = [
...commonFlyoutTabs,
{
id: ContentTabIds.LINK_TO_APM,
- name: i18n.translate('xpack.infra.nodeDetails.tabs.linkToApm', {
+ name: i18n.translate('xpack.infra.assetDetails.tabs.linkToApm', {
defaultMessage: 'APM',
}),
},
diff --git a/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts b/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts
index 12914892d97b4..a0922e794df71 100644
--- a/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts
+++ b/x-pack/plugins/observability_solution/infra/public/utils/filters/create_alerts_es_query.ts
@@ -8,9 +8,9 @@ import { getTime } from '@kbn/data-plugin/common';
import { ALERT_TIME_RANGE } from '@kbn/rule-data-utils';
import { BoolQuery, buildEsQuery, Filter, type TimeRange } from '@kbn/es-query';
import type { AlertStatus } from '@kbn/observability-plugin/common/typings';
+import { findInventoryFields } from '@kbn/metrics-data-access-plugin/common';
import { buildCombinedHostsFilter } from './build';
import { ALERT_STATUS_QUERY } from '../../components/shared/alerts/constants';
-import { HOST_NAME_FIELD } from '../../../common/constants';
export interface AlertsEsQuery {
bool: BoolQuery;
@@ -18,19 +18,19 @@ export interface AlertsEsQuery {
export const createAlertsEsQuery = ({
dateRange,
- hostNodeNames,
+ assetIds,
status,
}: {
dateRange: TimeRange;
- hostNodeNames: string[];
+ assetIds: string[];
status?: AlertStatus;
}): AlertsEsQuery => {
const alertStatusFilter = createAlertStatusFilter(status);
const dateFilter = createDateFilter(dateRange);
const hostsFilter = buildCombinedHostsFilter({
- field: HOST_NAME_FIELD,
- values: hostNodeNames,
+ field: findInventoryFields('host').id,
+ values: assetIds,
});
const filters = [alertStatusFilter, dateFilter, hostsFilter].filter(Boolean) as Filter[];
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/alerts_flyout_overview.tsx b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/alert_overview.tsx
similarity index 68%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/alerts_flyout_overview.tsx
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/alert_overview.tsx
index 126400187c8ca..a4cc5b30c3d53 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/alerts_flyout_overview.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/alert_overview.tsx
@@ -6,7 +6,14 @@
*/
import React, { memo, useEffect, useMemo, useState } from 'react';
-import { EuiInMemoryTable } from '@elastic/eui';
+import {
+ EuiTitle,
+ EuiSpacer,
+ EuiText,
+ EuiLink,
+ EuiHorizontalRule,
+ EuiInMemoryTable,
+} from '@elastic/eui';
import {
ALERT_CASE_IDS,
ALERT_DURATION,
@@ -24,27 +31,38 @@ import { useUiSetting } from '@kbn/kibana-react-plugin/public';
import { i18n } from '@kbn/i18n';
import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util';
-import { paths } from '../../../../common/locators/paths';
-import { TimeRange } from '../../../../common/custom_threshold_rule/types';
-import { TopAlert } from '../../../typings/alerts';
-import { useFetchBulkCases } from '../../../hooks/use_fetch_bulk_cases';
-import { useCaseViewNavigation } from '../../../hooks/use_case_view_navigation';
-import { useKibana } from '../../../utils/kibana_react';
+import { get } from 'lodash';
+import { paths } from '../../../common/locators/paths';
+import { TimeRange } from '../../../common/custom_threshold_rule/types';
+import { TopAlert } from '../../typings/alerts';
+import { useFetchBulkCases } from '../../hooks/use_fetch_bulk_cases';
+import { useCaseViewNavigation } from '../../hooks/use_case_view_navigation';
+import { useKibana } from '../../utils/kibana_react';
import {
FlyoutThresholdData,
mapRuleParamsWithFlyout,
} from './helpers/map_rules_params_with_flyout';
import { ColumnIDs, overviewColumns } from './overview_columns';
import { getSources } from './helpers/get_sources';
+import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
-export const Overview = memo(({ alert }: { alert: TopAlert }) => {
- const { http } = useKibana().services;
+export const AlertOverview = memo(({ alert, pageId }: { alert: TopAlert; pageId?: string }) => {
+ const {
+ http: {
+ basePath: { prepend },
+ },
+ } = useKibana().services;
const { cases, isLoading } = useFetchBulkCases({ ids: alert.fields[ALERT_CASE_IDS] || [] });
const dateFormat = useUiSetting('dateFormat');
const [timeRange, setTimeRange] = useState({ from: 'now-15m', to: 'now' });
const [ruleCriteria, setRuleCriteria] = useState([]);
const alertStart = alert.fields[ALERT_START];
const alertEnd = alert.fields[ALERT_END];
+ const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
+ const linkToRule =
+ pageId !== RULE_DETAILS_PAGE_ID && ruleId
+ ? prepend(paths.observability.ruleDetails(ruleId))
+ : null;
useEffect(() => {
const mappedRuleParams = mapRuleParamsWithFlyout(alert);
@@ -126,7 +144,7 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
meta: {
ruleLink:
alert.fields[ALERT_RULE_UUID] &&
- http.basePath.prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
+ prepend(paths.observability.ruleDetails(alert.fields[ALERT_RULE_UUID])),
},
},
{
@@ -154,11 +172,42 @@ export const Overview = memo(({ alert }: { alert: TopAlert }) => {
alertEnd,
cases,
dateFormat,
- http.basePath,
+ prepend,
isLoading,
navigateToCaseView,
ruleCriteria,
timeRange,
]);
- return ;
+
+ return (
+ <>
+
+
+ {i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
+ defaultMessage: 'Reason',
+ })}
+
+
+
+ {alert.reason}
+
+ {!!linkToRule && (
+
+ {i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
+ defaultMessage: 'View rule details',
+ })}
+
+ )}
+
+
+
+ {i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
+ defaultMessage: 'Document Summary',
+ })}
+
+
+
+
+ >
+ );
});
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/format_cases.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/format_cases.ts
similarity index 100%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/format_cases.ts
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/format_cases.ts
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/get_sources.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/get_sources.ts
similarity index 87%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/get_sources.ts
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/get_sources.ts
index 8acdcbf7b3da9..cfc64d9d414aa 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/get_sources.ts
+++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/get_sources.ts
@@ -9,8 +9,8 @@ import { ALERT_GROUP_FIELD, ALERT_GROUP_VALUE } from '@kbn/rule-data-utils';
import {
apmSources,
infraSources,
-} from '../../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
-import { TopAlert } from '../../../..';
+} from '../../../../common/custom_threshold_rule/helpers/get_alert_source_links';
+import { TopAlert } from '../../..';
interface AlertFields {
[key: string]: any;
@@ -36,7 +36,7 @@ export const getSources = (alert: TopAlert) => {
const fieldValue = alertFields[field];
matchedSources.push({
field: source,
- value: fieldValue[0],
+ value: Array.isArray(fieldValue) ? fieldValue[0] : fieldValue,
});
}
});
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/is_fields_same_type.ts b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/is_fields_same_type.ts
similarity index 100%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/is_fields_same_type.ts
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/is_fields_same_type.ts
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_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
similarity index 96%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/map_rules_params_with_flyout.test.ts
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.test.ts
index afe0c62042bda..706fce0c75a62 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_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
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { TopAlert } from '../../../../typings/alerts';
+import { TopAlert } from '../../../typings/alerts';
import { mapRuleParamsWithFlyout } from './map_rules_params_with_flyout';
describe('Map rules params with flyout', () => {
@@ -149,7 +149,7 @@ describe('Map rules params with flyout', () => {
observedValue: [4577],
threshold: [100],
comparator: 'more than',
- pctAboveThreshold: ' (4477.00% above the threshold)',
+ pctAboveThreshold: ' (4477% above the threshold)',
},
],
},
@@ -179,7 +179,7 @@ describe('Map rules params with flyout', () => {
observedValue: '6%',
threshold: '1%',
comparator: '>',
- pctAboveThreshold: ' (500.00% above the threshold)',
+ pctAboveThreshold: ' (500% above the threshold)',
},
],
},
@@ -242,7 +242,7 @@ describe('Map rules params with flyout', () => {
observedValue: [1],
threshold: [1],
comparator: '>',
- pctAboveThreshold: ' (0.00% above the threshold)',
+ pctAboveThreshold: ' (0% above the threshold)',
},
],
},
@@ -267,7 +267,7 @@ describe('Map rules params with flyout', () => {
observedValue: ['23 s'],
threshold: ['1.5 s'],
comparator: '>',
- pctAboveThreshold: ' (1424.80% above the threshold)',
+ pctAboveThreshold: ' (1424.8% above the threshold)',
},
],
},
@@ -291,7 +291,7 @@ describe('Map rules params with flyout', () => {
observedValue: ['25%'],
threshold: ['1.0%'],
comparator: '>',
- pctAboveThreshold: ' (2400.00% above the threshold)',
+ pctAboveThreshold: ' (2400% above the threshold)',
},
],
},
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_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
similarity index 92%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/helpers/map_rules_params_with_flyout.ts
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/helpers/map_rules_params_with_flyout.ts
index 9aa262936392a..f2f58e17ea56a 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_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,16 +19,16 @@ 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 { 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';
-import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../pages/alert_details/alert_details';
+import { asDuration, asPercent } 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';
+import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../pages/alert_details/alert_details';
import {
BaseMetricExpressionParams,
CustomMetricExpressionParams,
-} from '../../../../../common/custom_threshold_rule/types';
-import { TopAlert } from '../../../../typings/alerts';
+} from '../../../../common/custom_threshold_rule/types';
+import { TopAlert } from '../../../typings/alerts';
import { isFieldsSameType } from './is_fields_same_type';
export interface FlyoutThresholdData {
observedValue: string;
@@ -42,7 +42,7 @@ const getPctAboveThreshold = (observedValue?: number, threshold?: number[]): str
return i18n.translate('xpack.observability.alertFlyout.overview.aboveThresholdLabel', {
defaultMessage: ' ({pctValue}% above the threshold)',
values: {
- pctValue: (((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2),
+ pctValue: parseFloat((((observedValue - threshold[0]) * 100) / threshold[0]).toFixed(2)),
},
});
};
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/overview_columns.tsx b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx
similarity index 95%
rename from x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/overview_columns.tsx
rename to x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx
index 7694c49dba8a6..32820cb164309 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alert_flyout_overview/overview_columns.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/alert_overview/overview_columns.tsx
@@ -13,9 +13,9 @@ import { AlertStatus } from '@kbn/rule-data-utils';
import moment from 'moment';
import React from 'react';
import { Tooltip as CaseTooltip } from '@kbn/cases-components';
-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';
+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';
import { formatCase } from './helpers/format_cases';
import { FlyoutThresholdData } from './helpers/map_rules_params_with_flyout';
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_body.tsx b/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_body.tsx
index 1b56f512b0daf..7022a7fe55e7e 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_body.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_body.tsx
@@ -5,27 +5,12 @@
* 2.0.
*/
import React, { useCallback, useMemo, useState } from 'react';
-import { get } from 'lodash';
-import {
- EuiHorizontalRule,
- EuiLink,
- EuiPanel,
- EuiSpacer,
- EuiTabbedContentTab,
- EuiText,
- EuiTitle,
-} from '@elastic/eui';
-import { ALERT_RULE_UUID } from '@kbn/rule-data-utils';
+import { EuiPanel, EuiTabbedContentTab } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { AlertFieldsTable, ScrollableFlyoutTabbedContent } from '@kbn/alerts-ui-shared';
import { AlertsTableFlyoutBaseProps } from '@kbn/triggers-actions-ui-plugin/public';
-import { useKibana } from '../../utils/kibana_react';
-
-import { paths } from '../../../common/locators/paths';
-
-import { RULE_DETAILS_PAGE_ID } from '../../pages/rule_details/constants';
import type { TopAlert } from '../../typings/alerts';
-import { Overview } from './alert_flyout_overview/alerts_flyout_overview';
+import { AlertOverview } from '../alert_overview/alert_overview';
interface FlyoutProps {
rawAlert: AlertsTableFlyoutBaseProps['alert'];
@@ -36,18 +21,6 @@ interface FlyoutProps {
type TabId = 'overview' | 'table';
export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
- const {
- http: {
- basePath: { prepend },
- },
- } = useKibana().services;
-
- const ruleId = get(alert.fields, ALERT_RULE_UUID) ?? null;
- const linkToRule =
- pageId !== RULE_DETAILS_PAGE_ID && ruleId && prepend
- ? prepend(paths.observability.ruleDetails(ruleId))
- : null;
-
const overviewTab = useMemo(() => {
return {
id: 'overview',
@@ -57,37 +30,11 @@ export function AlertsFlyoutBody({ alert, rawAlert, id: pageId }: FlyoutProps) {
}),
content: (
-
-
- {i18n.translate('xpack.observability.alertsFlyout.reasonTitle', {
- defaultMessage: 'Reason',
- })}
-
-
-
- {alert.reason}
-
- {!!linkToRule && (
-
- {i18n.translate('xpack.observability.alertsFlyout.viewRulesDetailsLinkText', {
- defaultMessage: 'View rule details',
- })}
-
- )}
-
-
-
- {i18n.translate('xpack.observability.alertsFlyout.documentSummaryTitle', {
- defaultMessage: 'Document Summary',
- })}
-
-
-
-
+
),
};
- }, [alert, linkToRule]);
+ }, [alert, pageId]);
const metadataTab = useMemo(
() => ({
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_footer.tsx b/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_footer.tsx
index 324a96845137a..2d91fb7a9faf6 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_footer.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/alerts_flyout/alerts_flyout_footer.tsx
@@ -9,8 +9,6 @@ import React, { useState, useEffect } from 'react';
import { EuiFlyoutFooter, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useKibana } from '../../utils/kibana_react';
-import { usePluginContext } from '../../hooks/use_plugin_context';
-import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
import { paths } from '../../../common/locators/paths';
import type { TopAlert } from '../../typings/alerts';
@@ -25,7 +23,6 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
basePath: { prepend },
},
} = useKibana().services;
- const { config } = usePluginContext();
const [viewInAppUrl, setViewInAppUrl] = useState();
useEffect(() => {
@@ -48,23 +45,20 @@ export function AlertsFlyoutFooter({ alert, isInApp }: FlyoutProps & { isInApp:
)}
-
- {!isAlertDetailsEnabledPerApp(alert, config) ? null : (
-
-
- {i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
- defaultMessage: 'Alert details',
- })}
-
-
- )}
+
+
+ {i18n.translate('xpack.observability.alertsFlyout.alertsDetailsButtonText', {
+ defaultMessage: 'Alert details',
+ })}
+
+
);
diff --git a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/common/render_cell_value.tsx b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/common/render_cell_value.tsx
index d68b11115396e..5e4091a3080b0 100644
--- a/x-pack/plugins/observability_solution/observability/public/components/alerts_table/common/render_cell_value.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/components/alerts_table/common/render_cell_value.tsx
@@ -113,7 +113,7 @@ export const getRenderCellValue = ({
return (
setFlyoutAlert && setFlyoutAlert(alert.fields[ALERT_UUID])}
>
{alert.reason}
diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.test.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.test.tsx
index dbc084ee739bc..cbecbfa4530ce 100644
--- a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.test.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.test.tsx
@@ -16,6 +16,7 @@ import { waitFor } from '@testing-library/react';
import { Chance } from 'chance';
import React, { Fragment } from 'react';
import { useLocation, useParams } from 'react-router-dom';
+import { from } from 'rxjs';
import { useFetchAlertDetail } from '../../hooks/use_fetch_alert_detail';
import { ConfigSchema } from '../../plugin';
import { Subset } from '../../typings';
@@ -54,6 +55,7 @@ const mockKibana = () => {
services: {
...kibanaStartMock.startContract(),
cases: casesPluginMock.createStartContract(),
+ application: { currentAppId$: from('mockedApp') },
http: {
basePath: {
prepend: jest.fn(),
diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx
index 5781fc0c850d4..f9e49e670099b 100644
--- a/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/pages/alert_details/alert_details.tsx
@@ -38,12 +38,12 @@ import { PageTitle, pageTitleContent } from './components/page_title';
import { HeaderActions } from './components/header_actions';
import { AlertSummary, AlertSummaryField } from './components/alert_summary';
import { CenterJustifiedSpinner } from '../../components/center_justified_spinner';
-import PageNotFound from '../404';
import { getTimeZone } from '../../utils/get_time_zone';
import { isAlertDetailsEnabledPerApp } from '../../utils/is_alert_details_enabled';
import { observabilityFeatureId } from '../../../common';
import { paths } from '../../../common/locators/paths';
import { HeaderMenu } from '../overview/components/header_menu/header_menu';
+import { AlertOverview } from '../../components/alert_overview/alert_overview';
import { AlertDetailContextualInsights } from './alert_details_contextual_insights';
interface AlertDetailsPathParams {
@@ -133,11 +133,6 @@ export function AlertDetails() {
return ;
}
- // Redirect to the 404 page when the user hit the page url directly in the browser while the feature flag is off.
- if (alertDetail && !isAlertDetailsEnabledPerApp(alertDetail.formatted, config)) {
- return ;
- }
-
if (!isLoading && !alertDetail)
return (
@@ -167,27 +162,43 @@ export function AlertDetails() {
const OVERVIEW_TAB_ID = 'overview';
const METADATA_TAB_ID = 'metadata';
- const overviewTab = (
- <>
-
-
-
-
-
- {AlertDetailsAppSection && rule && alertDetail?.formatted && (
-
- )}
- >
+ const overviewTab = alertDetail ? (
+ AlertDetailsAppSection &&
+ /*
+ when feature flag is enabled, show alert details page with customized overview tab,
+ otherwise show default overview tab
+ */
+ isAlertDetailsEnabledPerApp(alertDetail.formatted, config) ? (
+ <>
+
+
+
+
+ {rule && alertDetail.formatted && (
+
+ )}
+ >
+ ) : (
+
+
+
+
+
+
+ )
+ ) : (
+ <>>
);
const metadataTab = alertDetail?.raw && (
-
+
+
);
diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.test.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.test.tsx
index 1122f190f93a7..71f7a3db59ee7 100644
--- a/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.test.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.test.tsx
@@ -147,8 +147,6 @@ describe('ObservabilityActions component', () => {
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
await waitFor(() => {
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(0);
- wrapper.update();
- expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeTruthy();
});
});
@@ -157,7 +155,15 @@ describe('ObservabilityActions component', () => {
wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
await waitFor(() => {
expect(wrapper.find('[data-test-subj~="viewRuleDetails"]').hostNodes().length).toBe(1);
- expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').hostNodes().length).toBe(1);
+ });
+ });
+
+ it('"View alert details" menu item should open alert details page', async () => {
+ const wrapper = await setup('nothing');
+ wrapper.find('[data-test-subj="alertsTableRowActionMore"]').hostNodes().simulate('click');
+ await waitFor(() => {
+ expect(wrapper.find('[data-test-subj~="viewAlertDetailsPage"]').hostNodes().length).toBe(1);
+ expect(wrapper.find('[data-test-subj~="viewAlertDetailsFlyout"]').exists()).toBeFalsy();
});
});
diff --git a/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.tsx
index 987cb698ea0b5..07383d9781c79 100644
--- a/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.tsx
+++ b/x-pack/plugins/observability_solution/observability/public/pages/alerts/components/alert_actions.tsx
@@ -25,7 +25,6 @@ import { useRouteMatch } from 'react-router-dom';
import { SLO_ALERTS_TABLE_ID } from '@kbn/observability-shared-plugin/common';
import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants';
import { paths, SLO_DETAIL_PATH } from '../../../../common/locators/paths';
-import { isAlertDetailsEnabledPerApp } from '../../../utils/is_alert_details_enabled';
import { useKibana } from '../../../utils/kibana_react';
import { parseAlert } from '../helpers/parse_alert';
import { observabilityFeatureId, ObservabilityRuleTypeRegistry } from '../../..';
@@ -147,7 +146,7 @@ export function AlertActions({
triggersActionsUi.getAlertsTableDefaultAlertActions({
key: 'defaultRowActions',
onActionExecuted: closeActionsPopover,
- isAlertDetailsEnabled: isAlertDetailsEnabledPerApp(observabilityAlert, config),
+ isAlertDetailsEnabled: true,
resolveRulePagePath: (ruleId, currentPageId) =>
currentPageId !== RULE_DETAILS_PAGE_ID ? paths.observability.ruleDetails(ruleId) : null,
resolveAlertPagePath: (alertId, currentPageId) =>
@@ -156,7 +155,7 @@ export function AlertActions({
: null,
...customActionsProps,
}),
- [config, customActionsProps, observabilityAlert, triggersActionsUi]
+ [customActionsProps, triggersActionsUi]
);
const actionsMenuItems = [
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/conversation_complete.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/conversation_complete.ts
index d1b08ed5283f5..eed16e9c8ddb4 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/conversation_complete.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/conversation_complete.ts
@@ -114,6 +114,8 @@ export enum ChatCompletionErrorCode {
InternalError = 'internalError',
NotFoundError = 'notFoundError',
TokenLimitReachedError = 'tokenLimitReachedError',
+ FunctionNotFoundError = 'functionNotFoundError',
+ FunctionLimitExceededError = 'functionLimitExceededError',
}
interface ErrorMetaAttributes {
@@ -123,6 +125,8 @@ interface ErrorMetaAttributes {
tokenLimit?: number;
tokenCount?: number;
};
+ [ChatCompletionErrorCode.FunctionNotFoundError]: {};
+ [ChatCompletionErrorCode.FunctionLimitExceededError]: {};
}
export class ChatCompletionError extends Error {
@@ -162,6 +166,20 @@ export function createInternalServerError(
return new ChatCompletionError(ChatCompletionErrorCode.InternalError, originalErrorMessage);
}
+export function createFunctionNotFoundError(name: string) {
+ return new ChatCompletionError(
+ ChatCompletionErrorCode.FunctionNotFoundError,
+ `Function ${name} called but was not available`
+ );
+}
+
+export function createFunctionLimitExceededError() {
+ return new ChatCompletionError(
+ ChatCompletionErrorCode.FunctionLimitExceededError,
+ `Function limit exceeded`
+ );
+}
+
export function isTokenLimitReachedError(
error: Error
): error is ChatCompletionError {
@@ -171,6 +189,15 @@ export function isTokenLimitReachedError(
);
}
+export function isFunctionNotFoundError(
+ error: Error
+): error is ChatCompletionError {
+ return (
+ error instanceof ChatCompletionError &&
+ error.code === ChatCompletionErrorCode.FunctionNotFoundError
+ );
+}
+
export function isChatCompletionError(error: Error): error is ChatCompletionError {
return error instanceof ChatCompletionError;
}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/index.ts
index 34ff261056dd4..66876bfe26085 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/index.ts
@@ -34,11 +34,13 @@ export {
createInternalServerError,
isTokenLimitReachedError,
isChatCompletionError,
+ createFunctionNotFoundError,
} from './conversation_complete';
export {
aiAssistantResponseLanguage,
aiAssistantLogsIndexPattern,
+ aiAssistantSimulatedFunctionCalling,
} from './ui_settings/settings_keys';
export { DEFAULT_LANGUAGE_OPTION, LANGUAGE_OPTIONS } from './ui_settings/language_options';
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/common/ui_settings/settings_keys.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/common/ui_settings/settings_keys.ts
index 27c1fc452c350..a57611079b279 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/common/ui_settings/settings_keys.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/common/ui_settings/settings_keys.ts
@@ -8,3 +8,5 @@
// AI Assistant
export const aiAssistantLogsIndexPattern = 'observability:aiAssistantLogsIndexPattern';
export const aiAssistantResponseLanguage = 'observability:aiAssistantResponseLanguage';
+export const aiAssistantSimulatedFunctionCalling =
+ 'observability:aiAssistantSimulatedFunctionCalling';
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/public/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/public/index.ts
index 56120ba9b25ca..91f1c640141f9 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/public/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/public/index.ts
@@ -82,6 +82,7 @@ export { LANGUAGE_OPTIONS, DEFAULT_LANGUAGE_OPTION } from '../common/ui_settings
export {
aiAssistantResponseLanguage,
aiAssistantLogsIndexPattern,
+ aiAssistantSimulatedFunctionCalling,
} from '../common/ui_settings/settings_keys';
export const plugin: PluginInitializer<
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/load_esql_docs/load_esql_docs.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/load_esql_docs/load_esql_docs.ts
index a3d1a07533956..f595c2ce422f5 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/load_esql_docs/load_esql_docs.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/scripts/load_esql_docs/load_esql_docs.ts
@@ -31,7 +31,7 @@ yargs(process.argv.slice(2))
(argv) => {
run(
async ({ log }) => {
- const builtDocsDir = Path.join(__dirname, '../../../../../../built-docs');
+ const builtDocsDir = Path.join(__dirname, '../../../../../../../built-docs');
log.debug(`Looking in ${builtDocsDir} for built-docs repository`);
@@ -205,7 +205,10 @@ yargs(process.argv.slice(2))
return !doc.title.startsWith('ES|QL');
});
- const outDir = Path.join(__dirname, '../../server/functions/esql/docs');
+ const outDir = Path.join(
+ __dirname,
+ '../../../observability_ai_assistant_app/server/functions/query/esql_docs'
+ );
log.info(`Writing ${flattened.length} documents to disk to ${outDir}`);
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts
index 232ea71fed75f..36d376a04abb3 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/functions/index.ts
@@ -50,7 +50,7 @@ export const registerFunctions: RegistrationCallback = async ({
If multiple functions are suitable, use the most specific and easy one. E.g., when the user asks to visualise APM data, use the APM functions (if available) rather than "query".
- Note that ES|QL (the Elasticsearch query language, which is NOT Elasticsearch SQL, but a new piped language) is the preferred query language.
+ Note that ES|QL (the Elasticsearch Query Language which is a new piped language) is the preferred query language.
You MUST use the "query" function when the user wants to:
- visualize data
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/index.ts
index 45f61713fc7f5..b67a9ab0a477c 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/index.ts
@@ -18,7 +18,11 @@ export type {
ObservabilityAIAssistantServerSetup,
} from './types';
-export { aiAssistantResponseLanguage, aiAssistantLogsIndexPattern } from '../common';
+export {
+ aiAssistantResponseLanguage,
+ aiAssistantLogsIndexPattern,
+ aiAssistantSimulatedFunctionCalling,
+} from '../common';
export const config: PluginConfigDescriptor = {
deprecations: ({ unusedFromRoot }) => [
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts
index 319ea153eea84..87e6294c694c4 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/routes/chat/route.ts
@@ -8,6 +8,7 @@ import { notImplemented } from '@hapi/boom';
import { toBooleanRt } from '@kbn/io-ts-utils';
import * as t from 'io-ts';
import { Readable } from 'stream';
+import { aiAssistantSimulatedFunctionCalling } from '../..';
import { flushBuffer } from '../../service/util/flush_buffer';
import { observableIntoStream } from '../../service/util/observable_into_stream';
import { createObservabilityAIAssistantServerRoute } from '../create_observability_ai_assistant_server_route';
@@ -42,11 +43,12 @@ const chatRoute = createObservabilityAIAssistantServerRoute({
]),
}),
handler: async (resources): Promise => {
- const { request, params, service } = resources;
+ const { request, params, service, context } = resources;
- const [client, cloudStart] = await Promise.all([
+ const [client, cloudStart, simulateFunctionCalling] = await Promise.all([
service.getClient({ request }),
resources.plugins.cloud?.start(),
+ (await context.core).uiSettings.client.get(aiAssistantSimulatedFunctionCalling),
]);
if (!client) {
@@ -73,6 +75,7 @@ const chatRoute = createObservabilityAIAssistantServerRoute({
functionCall,
}
: {}),
+ simulateFunctionCalling,
});
return observableIntoStream(response$.pipe(flushBuffer(!!cloudStart?.isCloudEnabled)));
@@ -109,11 +112,12 @@ const chatCompleteRoute = createObservabilityAIAssistantServerRoute({
]),
}),
handler: async (resources): Promise => {
- const { request, params, service } = resources;
+ const { request, params, service, context } = resources;
- const [client, cloudStart] = await Promise.all([
+ const [client, cloudStart, simulateFunctionCalling] = await Promise.all([
service.getClient({ request }),
resources.plugins.cloud?.start() || Promise.resolve(undefined),
+ (await context.core).uiSettings.client.get(aiAssistantSimulatedFunctionCalling),
]);
if (!client) {
@@ -156,6 +160,7 @@ const chatCompleteRoute = createObservabilityAIAssistantServerRoute({
functionClient,
responseLanguage,
instructions,
+ simulateFunctionCalling,
});
return observableIntoStream(response$.pipe(flushBuffer(!!cloudStart?.isCloudEnabled)));
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.test.ts
index 97f48257831b0..b45e6a91fb48c 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.test.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.test.ts
@@ -10,6 +10,7 @@ import { last } from 'lodash';
import { MessageRole } from '../../../../../common';
import { createBedrockClaudeAdapter } from './bedrock_claude_adapter';
import { LlmApiAdapterFactory } from '../types';
+import { TOOL_USE_END, TOOL_USE_START } from '../simulate_function_calling/constants';
describe('createBedrockClaudeAdapter', () => {
describe('getSubAction', () => {
@@ -65,24 +66,21 @@ describe('createBedrockClaudeAdapter', () => {
it('formats the functions', () => {
expect(callSubActionFactory().messages[0].content).toContain(
- dedent(`
-
- my_tool
- My tool
-
-
- myParam
- string
-
-
- Required: false
- Multiple: false
-
-
-
-
-
- `)
+ dedent(
+ JSON.stringify([
+ {
+ name: 'my_tool',
+ description: 'My tool',
+ parameters: {
+ properties: {
+ myParam: {
+ type: 'string',
+ },
+ },
+ },
+ },
+ ])
+ )
);
});
@@ -104,7 +102,7 @@ describe('createBedrockClaudeAdapter', () => {
expect(content).toContain(`"esql" tool`);
expect(content).not.toContain(`functions`);
expect(content).toContain(`tools`);
- expect(content).toContain(`function calls`);
+ expect(content).toContain(`tool calls`);
});
it('mentions to explicitly call the specified function if given', () => {
@@ -113,7 +111,7 @@ describe('createBedrockClaudeAdapter', () => {
);
});
- it('formats the function requests as XML', () => {
+ it('formats the function requests as JSON', () => {
const messages = [
{
'@timestamp': new Date().toISOString(),
@@ -136,14 +134,10 @@ describe('createBedrockClaudeAdapter', () => {
];
expect(last(callSubActionFactory({ messages }).messages)!.content).toContain(
- dedent(`
-
- my_tool
-
- myValue
-
-
- `)
+ dedent(`${TOOL_USE_START}
+ \`\`\`json
+ ${JSON.stringify({ name: 'my_tool', input: { myParam: 'myValue' } })}
+ \`\`\`${TOOL_USE_END}`)
);
});
@@ -177,16 +171,15 @@ describe('createBedrockClaudeAdapter', () => {
},
];
- expect(last(callSubActionFactory({ messages }).messages)!.content).toContain(
- dedent(`
-
- An internal server error occurred
-
- `)
- );
+ expect(JSON.parse(last(callSubActionFactory({ messages }).messages)!.content)).toEqual({
+ type: 'tool_result',
+ tool: 'my_tool',
+ error: 'An internal server error occurred',
+ is_error: true,
+ });
});
- it('formats function responses as XML + JSON', () => {
+ it('formats function responses as JSON', () => {
const messages = [
{
'@timestamp': new Date().toISOString(),
@@ -216,18 +209,11 @@ describe('createBedrockClaudeAdapter', () => {
},
];
- expect(last(callSubActionFactory({ messages }).messages)!.content).toContain(
- dedent(`
-
- my_tool
-
-
-myValue
-
-
-
- `)
- );
+ expect(JSON.parse(last(callSubActionFactory({ messages }).messages)!.content)).toEqual({
+ type: 'tool_result',
+ tool: 'my_tool',
+ myResponse: { myParam: 'myValue' },
+ });
});
});
});
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.ts
index f69e7235f9441..5c1713ffe6743 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/bedrock_claude_adapter.ts
@@ -5,18 +5,17 @@
* 2.0.
*/
-import dedent from 'dedent';
-import { castArray } from 'lodash';
import { filter, tap } from 'rxjs';
-import { Builder } from 'xml2js';
import { createInternalServerError } from '../../../../../common/conversation_complete';
import {
BedrockChunkMember,
eventstreamSerdeIntoObservable,
} from '../../../util/eventstream_serde_into_observable';
-import { jsonSchemaToFlatParameters } from '../../../util/json_schema_to_flat_parameters';
import { processBedrockStream } from './process_bedrock_stream';
import type { LlmApiAdapterFactory } from '../types';
+import { getMessagesWithSimulatedFunctionCalling } from '../simulate_function_calling/get_messages_with_simulated_function_calling';
+import { parseInlineFunctionCalls } from '../simulate_function_calling/parse_inline_function_calls';
+import { TOOL_USE_END } from '../simulate_function_calling/constants';
function replaceFunctionsWithTools(content: string) {
return content.replaceAll(/(function)(s|[\s*\.])?(?!\scall)/g, (match, p1, p2) => {
@@ -32,203 +31,46 @@ export const createBedrockClaudeAdapter: LlmApiAdapterFactory = ({
functions,
functionCall,
logger,
-}) => ({
- getSubAction: () => {
- const [systemMessage, ...otherMessages] = messages;
-
- const filteredFunctions = functionCall
- ? functions?.filter((fn) => fn.name === functionCall)
- : functions;
-
- let functionsPrompt: string = '';
-
- if (filteredFunctions?.length) {
- functionsPrompt = `In this environment, you have access to a set of tools you can use to answer the user's question.
-
- When deciding what tool to use, keep in mind that you can call other tools in successive requests, so decide what tool
- would be a good first step.
-
- You MUST only invoke a single tool, and invoke it once. Other invocations will be ignored.
- You MUST wait for the results before invoking another.
- You can call multiple tools in successive messages. This means you can chain function calls. If any tool was used in a previous
- message, consider whether it still makes sense to follow it up with another function call.
-
- ${
- functions?.find((fn) => fn.name === 'context')
- ? `The "context" function is ALWAYS used after a user question. Even if it was used before, your job is to answer the last user question,
- even if the "context" function was executed after that. Consider the tools you need to answer the user's question.`
- : ''
- }
-
- Rather than explaining how you would call a function, just generate the XML to call the function. It will automatically be
- executed and returned to you.
-
- These results are generally not visible to the user. Treat them as if they are not,
- unless specified otherwise.
-
- ONLY respond with XML, do not add any text.
-
- If a parameter allows multiple values, separate the values by ","
-
- You may call them like this.
-
-
-
- $TOOL_NAME
-
- <$PARAMETER_NAME>$PARAMETER_VALUE$PARAMETER_NAME>
- ...
-
-
-
-
- Here are the tools available:
-
-
- ${filteredFunctions
- .map(
- (fn) => `
- ${fn.name}
- ${fn.description}
- ${
- fn.parameters
- ? `
- ${jsonSchemaToFlatParameters(fn.parameters).map((param) => {
- return `
- ${param.name}
- ${param.type}
-
- ${param.description || ''}
- Required: ${!!param.required}
- Multiple: ${!!param.array}
- ${
- param.enum || param.constant
- ? `Allowed values: ${castArray(param.constant || param.enum).join(', ')}`
- : ''
- }
-
- `;
- })}
- `
- : ''
- }
- `
- )
- .join('\n')}
-
-
-
- Examples:
-
- Assistant:
-
-
- my_tool
-
- foo
-
-
-
-
- Assistant:
-
-
- another_tool
-
- foo
-
-
-
-
- `;
- } else {
- functionsPrompt = `No tools are available anymore. DO NOT UNDER ANY CIRCUMSTANCES call any tool, regardless of whether it was previously called.`;
- }
-
- const formattedMessages = [
- {
- role: 'system',
- content: `${replaceFunctionsWithTools(systemMessage.message.content!)}
-
- ${functionsPrompt}
- `,
- },
- ...otherMessages.map((message, index) => {
- const builder = new Builder({ headless: true });
- if (message.message.name) {
- const deserialized = JSON.parse(message.message.content || '{}');
-
- if ('error' in deserialized) {
- return {
- role: message.message.role,
- content: dedent(`
-
- ${builder.buildObject(deserialized)}
-
-
- `),
- };
- }
-
- return {
- role: message.message.role,
- content: dedent(`
-
-
- ${message.message.name}
-
- ${builder.buildObject(deserialized)}
-
-
- `),
- };
- }
-
- let content = replaceFunctionsWithTools(message.message.content || '');
-
- if (message.message.function_call?.name) {
- content += builder.buildObject({
- function_calls: {
- invoke: {
- tool_name: message.message.function_call.name,
- parameters: JSON.parse(message.message.function_call.arguments || '{}'),
- },
- },
- });
- }
-
- if (index === otherMessages.length - 1 && functionCall) {
- content += `
-
- Remember, use the ${functionCall} tool to answer this question.`;
- }
-
+}) => {
+ const filteredFunctions = functionCall
+ ? functions?.filter((fn) => fn.name === functionCall)
+ : functions;
+ return {
+ getSubAction: () => {
+ const messagesWithSimulatedFunctionCalling = getMessagesWithSimulatedFunctionCalling({
+ messages,
+ functions: filteredFunctions,
+ functionCall,
+ });
+
+ const formattedMessages = messagesWithSimulatedFunctionCalling.map((message) => {
return {
role: message.message.role,
- content,
+ content: replaceFunctionsWithTools(message.message.content ?? ''),
};
- }),
- ];
-
- return {
- subAction: 'invokeStream',
- subActionParams: {
- messages: formattedMessages,
- temperature: 0,
- stopSequences: ['\n\nHuman:', ''],
- },
- };
- },
- streamIntoObservable: (readable) =>
- eventstreamSerdeIntoObservable(readable, logger).pipe(
- tap((value) => {
- if ('modelStreamErrorException' in value) {
- throw createInternalServerError(value.modelStreamErrorException.originalMessage);
- }
- }),
- filter((value): value is BedrockChunkMember => {
- return 'chunk' in value && value.chunk?.headers?.[':event-type']?.value === 'chunk';
- }),
- processBedrockStream({ logger, functions })
- ),
-});
+ });
+
+ return {
+ subAction: 'invokeStream',
+ subActionParams: {
+ messages: formattedMessages,
+ temperature: 0,
+ stopSequences: ['\n\nHuman:', TOOL_USE_END],
+ },
+ };
+ },
+ streamIntoObservable: (readable) =>
+ eventstreamSerdeIntoObservable(readable, logger).pipe(
+ tap((value) => {
+ if ('modelStreamErrorException' in value) {
+ throw createInternalServerError(value.modelStreamErrorException.originalMessage);
+ }
+ }),
+ filter((value): value is BedrockChunkMember => {
+ return 'chunk' in value && value.chunk?.headers?.[':event-type']?.value === 'chunk';
+ }),
+ processBedrockStream(),
+ parseInlineFunctionCalls({ logger })
+ ),
+ };
+};
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.test.ts
index 69d37adec27c1..ee426db515322 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.test.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.test.ts
@@ -12,6 +12,8 @@ import { concatenateChatCompletionChunks } from '../../../../../common/utils/con
import { processBedrockStream } from './process_bedrock_stream';
import { MessageRole } from '../../../../../common';
import { rejectTokenCountEvents } from '../../../util/reject_token_count_events';
+import { TOOL_USE_END, TOOL_USE_START } from '../simulate_function_calling/constants';
+import { parseInlineFunctionCalls } from '../simulate_function_calling/parse_inline_function_calls';
describe('processBedrockStream', () => {
const encodeChunk = (body: unknown) => {
@@ -63,7 +65,10 @@ describe('processBedrockStream', () => {
encode(' text'),
stop()
).pipe(
- processBedrockStream({ logger: getLoggerMock() }),
+ processBedrockStream(),
+ parseInlineFunctionCalls({
+ logger: getLoggerMock(),
+ }),
rejectTokenCountEvents(),
concatenateChatCompletionChunks()
)
@@ -86,27 +91,15 @@ describe('processBedrockStream', () => {
await lastValueFrom(
of(
start(),
- encode('my_tool my_value '),
- stop(' ')
+ encode(TOOL_USE_START),
+ encode('```json\n'),
+ encode('{ "name": "my_tool", "input": { "my_param": "my_value" } }\n'),
+ encode('```'),
+ stop(TOOL_USE_END)
).pipe(
- processBedrockStream({
+ processBedrockStream(),
+ parseInlineFunctionCalls({
logger: getLoggerMock(),
- functions: [
- {
- name: 'my_tool',
- description: '',
- parameters: {
- properties: {
- my_param: {
- type: 'string',
- },
- },
- },
- },
- ],
}),
rejectTokenCountEvents(),
concatenateChatCompletionChunks()
@@ -131,28 +124,16 @@ describe('processBedrockStream', () => {
of(
start(),
encode('This is'),
- encode(' my text\nmy_tool my_value '),
- stop(' ')
+ encode(` my text${TOOL_USE_START.substring(0, 4)}`),
+ encode(`${TOOL_USE_START.substring(4)}\n\`\`\`json\n{"name":`),
+ encode(` "my_tool", "input`),
+ encode(`": { "my_param": "my_value" } }\n`),
+ encode('```'),
+ stop(TOOL_USE_END)
).pipe(
- processBedrockStream({
+ processBedrockStream(),
+ parseInlineFunctionCalls({
logger: getLoggerMock(),
- functions: [
- {
- name: 'my_tool',
- description: '',
- parameters: {
- properties: {
- my_param: {
- type: 'string',
- },
- },
- },
- },
- ],
}),
rejectTokenCountEvents(),
concatenateChatCompletionChunks()
@@ -171,33 +152,20 @@ describe('processBedrockStream', () => {
});
});
- it('throws an error if the XML cannot be parsed', async () => {
+ it('throws an error if the JSON cannot be parsed', async () => {
async function fn() {
return lastValueFrom(
of(
start(),
- encode('my_toolmy_value '),
- stop(' ')
+ encode(TOOL_USE_START),
+ encode('```json\n'),
+ encode('invalid json\n'),
+ encode('```'),
+ stop(TOOL_USE_END)
).pipe(
- processBedrockStream({
+ processBedrockStream(),
+ parseInlineFunctionCalls({
logger: getLoggerMock(),
- functions: [
- {
- name: 'my_tool',
- description: '',
- parameters: {
- properties: {
- my_param: {
- type: 'string',
- },
- },
- },
- },
- ],
}),
rejectTokenCountEvents(),
concatenateChatCompletionChunks()
@@ -205,77 +173,23 @@ describe('processBedrockStream', () => {
);
}
- await expect(fn).rejects.toThrowErrorMatchingInlineSnapshot(`
- "Unexpected close tag
- Line: 0
- Column: 49
- Char: >"
- `);
- });
-
- it('throws an error if the function does not exist', async () => {
- expect(
- async () =>
- await lastValueFrom(
- of(
- start(),
- encode('my_other_tool my_value '),
- stop(' ')
- ).pipe(
- processBedrockStream({
- logger: getLoggerMock(),
- functions: [
- {
- name: 'my_tool',
- description: '',
- parameters: {
- properties: {
- my_param: {
- type: 'string',
- },
- },
- },
- },
- ],
- }),
- rejectTokenCountEvents(),
- concatenateChatCompletionChunks()
- )
- )
- ).rejects.toThrowError(
- 'Function definition for my_other_tool not found. Available are: my_tool'
- );
+ await expect(fn).rejects.toThrowErrorMatchingInlineSnapshot(`"no elements in sequence"`);
});
it('successfully invokes a function without parameters', async () => {
expect(
await lastValueFrom(
of(
- encode('my_tool '),
- stop(' ')
+ start(),
+ encode(TOOL_USE_START),
+ encode('```json\n'),
+ encode('{ "name": "my_tool" }\n'),
+ encode('```'),
+ stop(TOOL_USE_END)
).pipe(
- processBedrockStream({
+ processBedrockStream(),
+ parseInlineFunctionCalls({
logger: getLoggerMock(),
- functions: [
- {
- name: 'my_tool',
- description: '',
- parameters: {
- properties: {
- my_param: {
- type: 'string',
- },
- },
- },
- },
- ],
}),
rejectTokenCountEvents(),
concatenateChatCompletionChunks()
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.ts
index 536d7fdc2ec0a..969bc5add78b1 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/bedrock/process_bedrock_stream.ts
@@ -7,17 +7,12 @@
import { Observable, Subscriber } from 'rxjs';
import { v4 } from 'uuid';
-import { Parser } from 'xml2js';
-import type { Logger } from '@kbn/logging';
-import type { JSONSchema } from 'json-schema-to-ts';
import {
ChatCompletionChunkEvent,
- createInternalServerError,
StreamingChatResponseEventType,
TokenCountEvent,
} from '../../../../../common/conversation_complete';
import type { BedrockChunkMember } from '../../../util/eventstream_serde_into_observable';
-import { convertDeserializedXmlWithJsonSchema } from '../../../util/convert_deserialized_xml_with_json_schema';
import { parseSerdeChunkBody } from './parse_serde_chunk_body';
import type {
CompletionChunk,
@@ -26,52 +21,9 @@ import type {
MessageStopChunk,
} from './types';
-async function parseFunctionCallXml({
- xml,
- functions,
-}: {
- xml: string;
- functions?: Array<{ name: string; description: string; parameters?: JSONSchema }>;
-}) {
- const parser = new Parser();
-
- const parsedValue = await parser.parseStringPromise(xml);
- const fnName = parsedValue.function_calls.invoke[0].tool_name[0];
- const parameters = (
- parsedValue.function_calls.invoke as Array<{ parameters: Array> }>
- ).flatMap((invoke) => invoke.parameters ?? []);
- const functionDef = functions?.find((fn) => fn.name === fnName);
-
- if (!functionDef) {
- throw createInternalServerError(
- `Function definition for ${fnName} not found. ${
- functions?.length
- ? 'Available are: ' + functions.map((fn) => fn.name).join(', ') + '.'
- : 'No functions are available.'
- }`
- );
- }
-
- const args = functionDef.parameters
- ? convertDeserializedXmlWithJsonSchema(parameters, functionDef.parameters)
- : {};
-
- return {
- name: fnName,
- arguments: JSON.stringify(args),
- };
-}
-
-export function processBedrockStream({
- logger,
- functions,
-}: {
- logger: Logger;
- functions?: Array<{ name: string; description: string; parameters?: JSONSchema }>;
-}) {
+export function processBedrockStream() {
return (source: Observable) =>
new Observable((subscriber) => {
- let functionCallsBuffer: string = '';
const id = v4();
// We use this to make sure we don't complete the Observable
@@ -98,63 +50,18 @@ export function processBedrockStream({
}
// completion: what we eventually want to emit
- let completion = chunkBody.type !== 'message_delta' ? getCompletion(chunkBody) : '';
-
- const isStartOfFunctionCall = !functionCallsBuffer && completion.includes('';
-
- const isInFunctionCall = !!functionCallsBuffer;
-
- if (isStartOfFunctionCall) {
- // when we see the start of the function call, split on {
- subscriber.next({
- id,
- type: StreamingChatResponseEventType.ChatCompletionChunk,
- message: {
- content: index === parts.length - 1 ? part : part + ' ',
- },
- });
- });
- }
+ const completion =
+ chunkBody.type !== 'message_delta'
+ ? getCompletion(chunkBody)
+ : chunkBody.delta.stop_sequence || '';
+
+ subscriber.next({
+ id,
+ type: StreamingChatResponseEventType.ChatCompletionChunk,
+ message: {
+ content: completion,
+ },
+ });
}
source.subscribe({
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/fail_on_non_existing_function_call.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/fail_on_non_existing_function_call.ts
new file mode 100644
index 0000000000000..1e99fd623052b
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/fail_on_non_existing_function_call.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { noop } from 'lodash';
+import { forkJoin, last, Observable, shareReplay, tap } from 'rxjs';
+import {
+ ChatCompletionChunkEvent,
+ createFunctionNotFoundError,
+ FunctionDefinition,
+} from '../../../../common';
+import { TokenCountEvent } from '../../../../common/conversation_complete';
+import { concatenateChatCompletionChunks } from '../../../../common/utils/concatenate_chat_completion_chunks';
+import { rejectTokenCountEvents } from '../../util/reject_token_count_events';
+
+export function failOnNonExistingFunctionCall({
+ functions,
+}: {
+ functions?: Array>;
+}) {
+ return (source$: Observable) => {
+ return new Observable((subscriber) => {
+ const shared = source$.pipe(shareReplay());
+
+ const checkFunctionCallResponse$ = shared.pipe(
+ rejectTokenCountEvents(),
+ concatenateChatCompletionChunks(),
+ last(),
+ tap((event) => {
+ if (
+ event.message.function_call.name &&
+ functions?.find((fn) => fn.name === event.message.function_call.name) === undefined
+ ) {
+ throw createFunctionNotFoundError(event.message.function_call.name);
+ }
+ })
+ );
+
+ source$.subscribe({
+ next: (val) => {
+ subscriber.next(val);
+ },
+ error: noop,
+ });
+
+ forkJoin([source$, checkFunctionCallResponse$]).subscribe({
+ complete: () => {
+ subscriber.complete();
+ },
+ error: (error) => {
+ subscriber.error(error);
+ },
+ });
+ });
+ };
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/openai_adapter.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/openai_adapter.ts
index 0c2e1e138192c..01b8fcb3b5923 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/openai_adapter.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/openai_adapter.ts
@@ -8,11 +8,14 @@
import { encode } from 'gpt-tokenizer';
import { compact, isEmpty, merge, omit, pick } from 'lodash';
import OpenAI from 'openai';
+import { identity } from 'rxjs';
import { CompatibleJSONSchema } from '../../../../common/functions/types';
import { Message, MessageRole } from '../../../../common';
import { processOpenAiStream } from './process_openai_stream';
import { eventsourceStreamIntoObservable } from '../../util/eventsource_stream_into_observable';
import { LlmApiAdapterFactory } from './types';
+import { parseInlineFunctionCalls } from './simulate_function_calling/parse_inline_function_calls';
+import { getMessagesWithSimulatedFunctionCalling } from './simulate_function_calling/get_messages_with_simulated_function_calling';
function getOpenAIPromptTokenCount({
messages,
@@ -54,40 +57,37 @@ function getOpenAIPromptTokenCount({
return tokensFromMessages + tokensFromFunctions;
}
+function messagesToOpenAI(messages: Message[]): OpenAI.ChatCompletionMessageParam[] {
+ return compact(
+ messages
+ .filter((message) => message.message.content || message.message.function_call?.name)
+ .map((message) => {
+ const role =
+ message.message.role === MessageRole.Elastic ? MessageRole.User : message.message.role;
+
+ return {
+ role,
+ content: message.message.content,
+ function_call: isEmpty(message.message.function_call?.name)
+ ? undefined
+ : omit(message.message.function_call, 'trigger'),
+ name: message.message.name,
+ } as OpenAI.ChatCompletionMessageParam;
+ })
+ );
+}
+
export const createOpenAiAdapter: LlmApiAdapterFactory = ({
messages,
functions,
functionCall,
logger,
+ simulateFunctionCalling,
}) => {
const promptTokens = getOpenAIPromptTokenCount({ messages, functions });
return {
getSubAction: () => {
- const messagesForOpenAI: Array<
- Omit & {
- role: MessageRole;
- }
- > = compact(
- messages
- .filter((message) => message.message.content || message.message.function_call?.name)
- .map((message) => {
- const role =
- message.message.role === MessageRole.Elastic
- ? MessageRole.User
- : message.message.role;
-
- return {
- role,
- content: message.message.content,
- function_call: isEmpty(message.message.function_call?.name)
- ? undefined
- : omit(message.message.function_call, 'trigger'),
- name: message.message.name,
- };
- })
- );
-
const functionsForOpenAI = functions?.map((fn) => ({
...fn,
parameters: merge(
@@ -99,22 +99,38 @@ export const createOpenAiAdapter: LlmApiAdapterFactory = ({
),
}));
- const request: Omit & { model?: string } = {
- messages: messagesForOpenAI as OpenAI.ChatCompletionCreateParams['messages'],
- stream: true,
- ...(!!functionsForOpenAI?.length
- ? {
- tools: functionsForOpenAI.map((fn) => ({
- function: pick(fn, 'name', 'description', 'parameters'),
- type: 'function',
- })),
- }
- : {}),
- temperature: 0,
- tool_choice: functionCall
- ? { function: { name: functionCall }, type: 'function' }
- : undefined,
- };
+ let request: Omit & { model?: string };
+
+ if (simulateFunctionCalling) {
+ request = {
+ messages: messagesToOpenAI(
+ getMessagesWithSimulatedFunctionCalling({
+ messages,
+ functions: functionsForOpenAI,
+ functionCall,
+ })
+ ),
+ stream: true,
+ temperature: 0,
+ };
+ } else {
+ request = {
+ messages: messagesToOpenAI(messages),
+ stream: true,
+ ...(!!functionsForOpenAI?.length
+ ? {
+ tools: functionsForOpenAI.map((fn) => ({
+ function: pick(fn, 'name', 'description', 'parameters'),
+ type: 'function',
+ })),
+ }
+ : {}),
+ temperature: 0,
+ tool_choice: functionCall
+ ? { function: { name: functionCall }, type: 'function' }
+ : undefined,
+ };
+ }
return {
subAction: 'stream',
@@ -126,7 +142,12 @@ export const createOpenAiAdapter: LlmApiAdapterFactory = ({
},
streamIntoObservable: (readable) => {
return eventsourceStreamIntoObservable(readable).pipe(
- processOpenAiStream({ promptTokenCount: promptTokens, logger })
+ processOpenAiStream({ promptTokenCount: promptTokens, logger }),
+ simulateFunctionCalling
+ ? parseInlineFunctionCalls({
+ logger,
+ })
+ : identity
);
},
};
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/constants.ts
similarity index 65%
rename from x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.ts
rename to x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/constants.ts
index 7a13e661e914e..a25deca07b7d9 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/common/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/constants.ts
@@ -5,9 +5,5 @@
* 2.0.
*/
-import type { Maybe } from '../../../../common';
-
-export interface HostsKpiHistogramData {
- x?: Maybe;
- y?: Maybe;
-}
+export const TOOL_USE_START = '<|tool_use_start|>';
+export const TOOL_USE_END = '<|tool_use_end|>';
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_messages_with_simulated_function_calling.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_messages_with_simulated_function_calling.ts
new file mode 100644
index 0000000000000..2e319a076773c
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_messages_with_simulated_function_calling.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { FunctionDefinition, Message } from '../../../../../common';
+import { TOOL_USE_END, TOOL_USE_START } from './constants';
+import { getSystemMessageInstructions } from './get_system_message_instructions';
+
+export function getMessagesWithSimulatedFunctionCalling({
+ messages,
+ functions,
+ functionCall,
+}: {
+ messages: Message[];
+ functions?: Array>;
+ functionCall?: string;
+}): Message[] {
+ const [systemMessage, ...otherMessages] = messages;
+
+ const instructions = getSystemMessageInstructions({
+ functions,
+ });
+
+ systemMessage.message.content = (systemMessage.message.content ?? '') + '\n' + instructions;
+
+ return [systemMessage, ...otherMessages].map((message, index) => {
+ if (message.message.name) {
+ const deserialized = JSON.parse(message.message.content || '{}');
+
+ const results = {
+ type: 'tool_result',
+ tool: message.message.name,
+ ...(message.message.content ? JSON.parse(message.message.content) : {}),
+ };
+
+ if ('error' in deserialized) {
+ return {
+ ...message,
+ message: {
+ role: message.message.role,
+ content: JSON.stringify({
+ ...results,
+ is_error: true,
+ }),
+ },
+ };
+ }
+
+ return {
+ ...message,
+ message: {
+ role: message.message.role,
+ content: JSON.stringify(results),
+ },
+ };
+ }
+
+ let content = message.message.content || '';
+
+ if (message.message.function_call?.name) {
+ content +=
+ TOOL_USE_START +
+ '\n```json\n' +
+ JSON.stringify({
+ name: message.message.function_call.name,
+ input: JSON.parse(message.message.function_call.arguments || '{}'),
+ }) +
+ '\n```' +
+ TOOL_USE_END;
+ }
+
+ if (index === messages.length - 1 && functionCall) {
+ content += `
+
+ Remember, use the ${functionCall} tool to answer this question.`;
+ }
+
+ return {
+ ...message,
+ message: {
+ role: message.message.role,
+ content,
+ },
+ };
+ });
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_system_message_instructions.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_system_message_instructions.ts
new file mode 100644
index 0000000000000..dd74052ce7bbe
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/get_system_message_instructions.ts
@@ -0,0 +1,99 @@
+/*
+ * 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 { FunctionDefinition } from '../../../../../common';
+import { TOOL_USE_END, TOOL_USE_START } from './constants';
+
+export function getSystemMessageInstructions({
+ functions,
+}: {
+ functions?: Array>;
+}) {
+ if (functions?.length) {
+ return `In this environment, you have access to a set of tools you can use to answer the user's question.
+
+ When deciding what tool to use, keep in mind that you can call other tools in successive requests, so decide what tool
+ would be a good first step.
+
+ You MUST only invoke a single tool, and invoke it once. Other invocations will be ignored.
+ You MUST wait for the results before invoking another.
+ You can call multiple tools in successive messages. This means you can chain tool calls. If any tool was used in a previous
+ message, consider whether it still makes sense to follow it up with another tool call.
+
+ ${
+ functions?.find((fn) => fn.name === 'context')
+ ? `The "context" tool is ALWAYS used after a user question. Even if it was used before, your job is to answer the last user question,
+ even if the "context" tool was executed after that. Consider the tools you need to answer the user's question.`
+ : ''
+ }
+
+ Rather than explaining how you would call a tool, just generate the JSON to call the tool. It will automatically be
+ executed and returned to you.
+
+ These results are generally not visible to the user. Treat them as if they are not,
+ unless specified otherwise.
+
+ You may call them like this.
+
+ Given the following tool:
+
+ {
+ "name": "my_tool",
+ "description: "A tool to call",
+ "parameters": {
+ "type": "object",
+ "properties": {
+ "myProperty": {
+ "type": "string"
+ }
+ }
+ }
+ }
+
+ Use it the following way:
+
+ ${TOOL_USE_START}
+ \`\`\`json
+ {
+ "name": "my_tool",
+ "input": {
+ "myProperty": "myValue"
+ }
+ }
+ \`\`\`\
+ ${TOOL_USE_END}
+
+ Given the following tool:
+ {
+ "name": "my_tool_without_parameters",
+ "description": "A tool to call without parameters",
+ }
+
+ Use it the following way:
+ ${TOOL_USE_START}
+ \`\`\`json
+ {
+ "name": "my_tool_without_parameters"
+ }
+ \`\`\`\
+ ${TOOL_USE_END}
+
+ Here are the tools available:
+
+ ${JSON.stringify(
+ functions.map((fn) => ({
+ name: fn.name,
+ description: fn.description,
+ ...(fn.parameters ? { parameters: fn.parameters } : {}),
+ }))
+ )}
+
+ `;
+ }
+
+ return `No tools are available anymore. Ignore everything that was said about tools before. DO NOT UNDER ANY CIRCUMSTANCES call any tool, regardless of whether it was previously called.`;
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/parse_inline_function_calls.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/parse_inline_function_calls.ts
new file mode 100644
index 0000000000000..db32eb581249c
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/simulate_function_calling/parse_inline_function_calls.ts
@@ -0,0 +1,132 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { Observable } from 'rxjs';
+import { Logger } from '@kbn/logging';
+import {
+ ChatCompletionChunkEvent,
+ createInternalServerError,
+ StreamingChatResponseEventType,
+} from '../../../../../common';
+import { TokenCountEvent } from '../../../../../common/conversation_complete';
+import { TOOL_USE_END, TOOL_USE_START } from './constants';
+
+function matchOnSignalStart(buffer: string) {
+ if (buffer.includes(TOOL_USE_START)) {
+ const split = buffer.split(TOOL_USE_START);
+ return [split[0], TOOL_USE_START + split[1]];
+ }
+
+ for (let i = 0; i < buffer.length; i++) {
+ const remaining = buffer.substring(i);
+ if (TOOL_USE_START.startsWith(remaining)) {
+ return [buffer.substring(0, i), remaining];
+ }
+ }
+
+ return false;
+}
+
+export function parseInlineFunctionCalls({ logger }: { logger: Logger }) {
+ return (source: Observable) => {
+ let functionCallBuffer: string = '';
+
+ // As soon as we see a TOOL_USE_START token, we write all chunks
+ // to a buffer, that we flush as a function request if we
+ // spot the stop sequence.
+
+ return new Observable((subscriber) => {
+ function parseFunctionCall(id: string, buffer: string) {
+ logger.debug('Parsing function call:\n' + buffer);
+
+ const functionCallBody = buffer
+ .replace(TOOL_USE_START, '')
+ .replace(TOOL_USE_END, '')
+ .trim()
+ .replace(/^```(json?)/, '')
+ .replace(/```$/, '')
+ .trim();
+
+ const parsedFunctionCall = JSON.parse(functionCallBody) as {
+ name?: string;
+ input?: unknown;
+ };
+
+ logger.debug('Parsed function call:\n ' + JSON.stringify(parsedFunctionCall));
+
+ if (!parsedFunctionCall.name) {
+ throw createInternalServerError(`Missing name for tool use`);
+ }
+
+ subscriber.next({
+ id,
+ message: {
+ content: '',
+ function_call: {
+ name: parsedFunctionCall.name,
+ arguments: JSON.stringify(parsedFunctionCall.input || {}),
+ },
+ },
+ type: StreamingChatResponseEventType.ChatCompletionChunk,
+ });
+ }
+
+ source.subscribe({
+ next: (event) => {
+ if (event.type === StreamingChatResponseEventType.TokenCount) {
+ subscriber.next(event);
+ return;
+ }
+
+ const { type, id, message } = event;
+
+ function next(content: string) {
+ subscriber.next({
+ id,
+ type,
+ message: {
+ ...message,
+ content,
+ },
+ });
+ }
+
+ const content = message.content ?? '';
+
+ const match = matchOnSignalStart(functionCallBuffer + content);
+
+ if (match) {
+ const [beforeStartSignal, afterStartSignal] = match;
+ functionCallBuffer = afterStartSignal;
+ if (beforeStartSignal) {
+ next(beforeStartSignal);
+ }
+
+ if (functionCallBuffer.includes(TOOL_USE_END)) {
+ const [beforeEndSignal, afterEndSignal] = functionCallBuffer.split(TOOL_USE_END);
+
+ parseFunctionCall(id, beforeEndSignal + TOOL_USE_END);
+
+ functionCallBuffer = '';
+
+ next(afterEndSignal);
+ }
+ } else {
+ functionCallBuffer = '';
+ next(content);
+ }
+ },
+ complete: () => {
+ subscriber.complete();
+ },
+ error: (error) => {
+ subscriber.error(error);
+ },
+ });
+ });
+ };
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/types.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/types.ts
index b9f1403cb5e8e..3dd2c4bbed5f3 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/types.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/adapters/types.ts
@@ -26,6 +26,7 @@ export type LlmApiAdapterFactory = (options: {
messages: Message[];
functions?: Array<{ name: string; description: string; parameters?: CompatibleJSONSchema }>;
functionCall?: string;
+ simulateFunctionCalling?: boolean;
}) => LlmApiAdapter;
export interface LlmApiAdapter {
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts
index 424235c472317..2f6093e860617 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.test.ts
@@ -750,7 +750,7 @@ describe('Observability AI Assistant client', () => {
await llmSimulator.next({
content: 'Hello',
- function_call: { name: 'my-function', arguments: JSON.stringify({ foo: 'bar' }) },
+ function_call: { name: 'myFunction', arguments: JSON.stringify({ foo: 'bar' }) },
});
const prevLlmSimulator = llmSimulator;
@@ -780,7 +780,7 @@ describe('Observability AI Assistant client', () => {
content: 'Hello',
role: MessageRole.Assistant,
function_call: {
- name: 'my-function',
+ name: 'myFunction',
arguments: JSON.stringify({ foo: 'bar' }),
trigger: MessageRole.Assistant,
},
@@ -792,7 +792,7 @@ describe('Observability AI Assistant client', () => {
it('executes the function', () => {
expect(functionClientMock.executeFunction).toHaveBeenCalledWith({
connectorId: 'foo',
- name: 'my-function',
+ name: 'myFunction',
chat: expect.any(Function),
args: JSON.stringify({ foo: 'bar' }),
signal: expect.any(AbortSignal),
@@ -818,7 +818,7 @@ describe('Observability AI Assistant client', () => {
role: MessageRole.Assistant,
content: 'Hello',
function_call: {
- name: 'my-function',
+ name: 'myFunction',
arguments: JSON.stringify({ foo: 'bar' }),
trigger: MessageRole.Assistant,
},
@@ -851,7 +851,7 @@ describe('Observability AI Assistant client', () => {
'@timestamp': expect.any(String),
message: {
role: MessageRole.User,
- name: 'my-function',
+ name: 'myFunction',
content: JSON.stringify({
my: 'content',
}),
@@ -952,7 +952,7 @@ describe('Observability AI Assistant client', () => {
content: 'Hello',
role: MessageRole.Assistant,
function_call: {
- name: 'my-function',
+ name: 'myFunction',
arguments: JSON.stringify({ foo: 'bar' }),
trigger: MessageRole.Assistant,
},
@@ -964,7 +964,7 @@ describe('Observability AI Assistant client', () => {
content: JSON.stringify({
my: 'content',
}),
- name: 'my-function',
+ name: 'myFunction',
role: MessageRole.User,
},
},
@@ -999,7 +999,7 @@ describe('Observability AI Assistant client', () => {
'@timestamp': expect.any(String),
message: {
role: MessageRole.User,
- name: 'my-function',
+ name: 'myFunction',
content: JSON.stringify({
message: 'Error: Function failed',
error: {},
@@ -1034,7 +1034,7 @@ describe('Observability AI Assistant client', () => {
await nextTick();
- response$.next(createFunctionResponseMessage({ name: 'my-function', content: {} }));
+ response$.next(createFunctionResponseMessage({ name: 'myFunction', content: {} }));
});
it('appends the function response', async () => {
@@ -1045,7 +1045,7 @@ describe('Observability AI Assistant client', () => {
'@timestamp': expect.any(String),
message: {
role: MessageRole.User,
- name: 'my-function',
+ name: 'myFunction',
content: '{}',
},
},
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts
index 4f192c80efab1..2c0af46318937 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/client/index.ts
@@ -14,6 +14,7 @@ import apm from 'elastic-apm-node';
import { decode, encode } from 'gpt-tokenizer';
import { findLastIndex, last, merge, noop, omit, pick, take } from 'lodash';
import {
+ catchError,
filter,
isObservable,
last as lastOperator,
@@ -32,7 +33,9 @@ import {
ChatCompletionChunkEvent,
ChatCompletionErrorEvent,
createConversationNotFoundError,
+ createFunctionLimitExceededError,
createTokenLimitReachedError,
+ isFunctionNotFoundError,
MessageAddEvent,
StreamingChatResponseEventType,
TokenCountEvent,
@@ -67,6 +70,7 @@ import { rejectTokenCountEvents } from '../util/reject_token_count_events';
import { createBedrockClaudeAdapter } from './adapters/bedrock/bedrock_claude_adapter';
import { createOpenAiAdapter } from './adapters/openai_adapter';
import { LlmApiAdapter } from './adapters/types';
+import { failOnNonExistingFunctionCall } from './adapters/fail_on_non_existing_function_call';
export class ObservabilityAIAssistantClient {
constructor(
@@ -153,10 +157,12 @@ export class ObservabilityAIAssistantClient {
conversationId?: string;
title?: string;
instructions?: Array;
+ simulateFunctionCalling?: boolean;
}): Observable> => {
return new Observable>(
(subscriber) => {
- const { messages, connectorId, signal, functionClient, persist } = params;
+ const { messages, connectorId, signal, functionClient, persist, simulateFunctionCalling } =
+ params;
const conversationId = params.conversationId || '';
const title = params.title || '';
@@ -169,8 +175,11 @@ export class ObservabilityAIAssistantClient {
total: 0,
};
- const chatWithTokenCountIncrement: ChatFn = async (...chatArgs) => {
- const response$ = await this.chat(...chatArgs);
+ const chatWithTokenCountIncrement: ChatFn = async (name, options) => {
+ const response$ = await this.chat(name, {
+ ...options,
+ simulateFunctionCalling,
+ });
const incrementTokenCount = () => {
return (
@@ -253,7 +262,7 @@ export class ObservabilityAIAssistantClient {
return await next(nextMessages.concat(contextFunctionRequest));
} else if (isUserMessage) {
const functions =
- numFunctionsCalled === MAX_FUNCTION_CALLS ? [] : allFunctions.concat(allActions);
+ numFunctionsCalled >= MAX_FUNCTION_CALLS ? [] : allFunctions.concat(allActions);
const spanName =
lastMessage.message.name && lastMessage.message.name !== 'context'
@@ -267,7 +276,16 @@ export class ObservabilityAIAssistantClient {
signal,
functions,
})
- ).pipe(emitWithConcatenatedMessage(), shareReplay());
+ ).pipe(
+ emitWithConcatenatedMessage(),
+ shareReplay(),
+ catchError((error) => {
+ if (isFunctionNotFoundError(error) && functions.length === 0) {
+ throw createFunctionLimitExceededError();
+ }
+ throw error;
+ })
+ );
response$.subscribe({
next: (val) => subscriber.next(val),
@@ -565,12 +583,14 @@ export class ObservabilityAIAssistantClient {
functions,
functionCall,
signal,
+ simulateFunctionCalling,
}: {
messages: Message[];
connectorId: string;
functions?: Array<{ name: string; description: string; parameters?: CompatibleJSONSchema }>;
functionCall?: string;
signal: AbortSignal;
+ simulateFunctionCalling?: boolean;
}
): Promise> => {
const span = apm.startSpan(`chat ${name}`);
@@ -595,6 +615,7 @@ export class ObservabilityAIAssistantClient {
functions,
functionCall,
logger: this.dependencies.logger,
+ simulateFunctionCalling,
});
break;
@@ -650,7 +671,19 @@ export class ObservabilityAIAssistantClient {
signal.addEventListener('abort', () => response.destroy());
- const response$ = adapter.streamIntoObservable(response).pipe(shareReplay());
+ const response$ = adapter.streamIntoObservable(response).pipe(
+ shareReplay(),
+ failOnNonExistingFunctionCall({ functions }),
+ tap((event) => {
+ if (event.type === StreamingChatResponseEventType.TokenCount) {
+ span?.addLabels({
+ tokenCountPrompt: event.tokens.prompt,
+ tokenCountCompletion: event.tokens.completion,
+ tokenCountTotal: event.tokens.total,
+ });
+ }
+ })
+ );
response$
.pipe(rejectTokenCountEvents(), concatenateChatCompletionChunks(), lastOperator())
@@ -670,19 +703,6 @@ export class ObservabilityAIAssistantClient {
},
});
- response$.subscribe({
- next: (event) => {
- if (event.type === StreamingChatResponseEventType.TokenCount) {
- span?.addLabels({
- tokenCountPrompt: event.tokens.prompt,
- tokenCountCompletion: event.tokens.completion,
- tokenCountTotal: event.tokens.total,
- });
- }
- },
- error: () => {},
- });
-
return response$;
} catch (error) {
span?.setOutcome('failure');
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.test.ts
deleted file mode 100644
index 8d1d64721abc4..0000000000000
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.test.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import { convertDeserializedXmlWithJsonSchema } from './convert_deserialized_xml_with_json_schema';
-
-describe('deserializeXmlWithJsonSchema', () => {
- it('deserializes XML into a JSON object according to the JSON schema', () => {
- expect(
- convertDeserializedXmlWithJsonSchema(
- [
- {
- foo: ['bar'],
- },
- ],
- {
- type: 'object',
- properties: {
- foo: {
- type: 'string',
- },
- },
- }
- )
- ).toEqual({ foo: 'bar' });
- });
-
- it('converts strings to numbers if needed', () => {
- expect(
- convertDeserializedXmlWithJsonSchema(
- [
- {
- myNumber: ['0'],
- },
- ],
- {
- type: 'object',
- properties: {
- myNumber: {
- type: 'number',
- },
- },
- }
- )
- ).toEqual({ myNumber: 0 });
- });
-
- it('de-dots object paths', () => {
- expect(
- convertDeserializedXmlWithJsonSchema(
- [
- {
- 'myObject.foo': ['bar'],
- },
- ],
- {
- type: 'object',
- properties: {
- myObject: {
- type: 'object',
- properties: {
- foo: {
- type: 'string',
- },
- },
- },
- },
- }
- )
- ).toEqual({
- myObject: {
- foo: 'bar',
- },
- });
- });
-
- it('casts to an array if needed', () => {
- expect(
- convertDeserializedXmlWithJsonSchema(
- [
- {
- myNumber: ['0'],
- },
- ],
- {
- type: 'object',
- properties: {
- myNumber: {
- type: 'number',
- },
- },
- }
- )
- ).toEqual({
- myNumber: 0,
- });
-
- expect(
- convertDeserializedXmlWithJsonSchema(
- [
- {
- 'labels.myProp': ['myFirstValue, mySecondValue'],
- },
- ],
- {
- type: 'object',
- properties: {
- labels: {
- type: 'array',
- items: {
- type: 'object',
- properties: {
- myProp: {
- type: 'string',
- },
- },
- },
- },
- },
- }
- )
- ).toEqual({
- labels: [{ myProp: 'myFirstValue' }, { myProp: 'mySecondValue' }],
- });
- });
-});
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.ts
deleted file mode 100644
index a351edb9a33a1..0000000000000
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/convert_deserialized_xml_with_json_schema.ts
+++ /dev/null
@@ -1,106 +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 { set } from '@kbn/safer-lodash-set';
-import { unflatten } from 'flat';
-import type { JSONSchema } from 'json-schema-to-ts';
-import { forEach, get, isPlainObject } from 'lodash';
-import { jsonSchemaToFlatParameters } from './json_schema_to_flat_parameters';
-
-// JS to XML is "lossy", e.g. everything becomes an array and a string,
-// so we need a JSON schema to deserialize it
-
-export function convertDeserializedXmlWithJsonSchema(
- parameterResults: Array>,
- schema: JSONSchema
-): Record {
- const parameters = jsonSchemaToFlatParameters(schema);
-
- const result: Record = Object.fromEntries(
- parameterResults.flatMap((parameterResult) => {
- return Object.keys(parameterResult).map((name) => {
- return [name, parameterResult[name]];
- });
- })
- );
-
- parameters.forEach((param) => {
- const key = param.name;
- let value: any[] = result[key] ?? [];
- value = param.array
- ? String(value)
- .split(',')
- .map((val) => val.trim())
- : value;
-
- switch (param.type) {
- case 'number':
- value = value.map((val) => Number(val));
- break;
-
- case 'integer':
- value = value.map((val) => Math.floor(Number(val)));
- break;
-
- case 'boolean':
- value = value.map((val) => String(val).toLowerCase() === 'true' || val === '1');
- break;
- }
-
- result[key] = param.array ? value : value[0];
- });
-
- function getArrayPaths(subSchema: JSONSchema, path: string = ''): string[] {
- if (typeof subSchema === 'boolean') {
- return [];
- }
-
- if (subSchema.type === 'object') {
- return Object.keys(subSchema.properties!).flatMap((key) => {
- return getArrayPaths(subSchema.properties![key], path ? path + '.' + key : key);
- });
- }
-
- if (subSchema.type === 'array') {
- return [path, ...getArrayPaths(subSchema.items as JSONSchema, path)];
- }
-
- return [];
- }
-
- const arrayPaths = getArrayPaths(schema);
-
- const unflattened: Record = unflatten(result);
-
- arrayPaths.forEach((arrayPath) => {
- const target: any[] = [];
- function walk(value: any, path: string) {
- if (Array.isArray(value)) {
- value.forEach((val, index) => {
- if (!target[index]) {
- target[index] = {};
- }
- if (path) {
- set(target[index], path, val);
- } else {
- target[index] = val;
- }
- });
- } else if (isPlainObject(value)) {
- forEach(value, (val, key) => {
- walk(val, path ? path + '.' + key : key);
- });
- }
- }
- const val = get(unflattened, arrayPath);
-
- walk(val, '');
-
- set(unflattened, arrayPath, target);
- });
-
- return unflattened;
-}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.test.ts
deleted file mode 100644
index afcfedf71dc85..0000000000000
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.test.ts
+++ /dev/null
@@ -1,208 +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 { jsonSchemaToFlatParameters } from './json_schema_to_flat_parameters';
-
-describe('jsonSchemaToFlatParameters', () => {
- it('converts a simple object', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- str: {
- type: 'string',
- },
- bool: {
- type: 'boolean',
- },
- },
- })
- ).toEqual([
- {
- name: 'str',
- type: 'string',
- required: false,
- },
- {
- name: 'bool',
- type: 'boolean',
- required: false,
- },
- ]);
- });
-
- it('handles descriptions', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- str: {
- type: 'string',
- description: 'My string',
- },
- },
- })
- ).toEqual([
- {
- name: 'str',
- type: 'string',
- required: false,
- description: 'My string',
- },
- ]);
- });
-
- it('handles required properties', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- str: {
- type: 'string',
- },
- bool: {
- type: 'boolean',
- },
- },
- required: ['str'],
- })
- ).toEqual([
- {
- name: 'str',
- type: 'string',
- required: true,
- },
- {
- name: 'bool',
- type: 'boolean',
- required: false,
- },
- ]);
- });
-
- it('handles objects', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- nested: {
- type: 'object',
- properties: {
- str: {
- type: 'string',
- },
- },
- },
- },
- required: ['str'],
- })
- ).toEqual([
- {
- name: 'nested.str',
- required: false,
- type: 'string',
- },
- ]);
- });
-
- it('handles arrays', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- arr: {
- type: 'array',
- items: {
- type: 'string',
- },
- },
- },
- required: ['str'],
- })
- ).toEqual([
- {
- name: 'arr',
- required: false,
- array: true,
- type: 'string',
- },
- ]);
-
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- arr: {
- type: 'array',
- items: {
- type: 'object',
- properties: {
- foo: {
- type: 'string',
- },
- bar: {
- type: 'object',
- properties: {
- baz: {
- type: 'string',
- },
- },
- },
- },
- },
- },
- },
- required: ['arr.foo.bar'],
- })
- ).toEqual([
- {
- name: 'arr.foo',
- required: false,
- array: true,
- type: 'string',
- },
- {
- name: 'arr.bar.baz',
- required: false,
- array: true,
- type: 'string',
- },
- ]);
- });
-
- it('handles enum and const', () => {
- expect(
- jsonSchemaToFlatParameters({
- type: 'object',
- properties: {
- constant: {
- type: 'string',
- const: 'foo',
- },
- enum: {
- type: 'number',
- enum: ['foo', 'bar'],
- },
- },
- required: ['str'],
- })
- ).toEqual([
- {
- name: 'constant',
- required: false,
- type: 'string',
- constant: 'foo',
- },
- {
- name: 'enum',
- required: false,
- type: 'number',
- enum: ['foo', 'bar'],
- },
- ]);
- });
-});
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.ts
deleted file mode 100644
index cd984b0cfd7d0..0000000000000
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/util/json_schema_to_flat_parameters.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { JSONSchema } from 'json-schema-to-ts';
-import { castArray, isArray } from 'lodash';
-
-interface Parameter {
- name: string;
- type: string;
- description?: string;
- required?: boolean;
- enum?: unknown[];
- constant?: unknown;
- array?: boolean;
-}
-
-export function jsonSchemaToFlatParameters(
- schema: JSONSchema,
- name: string = '',
- options: { required?: boolean; array?: boolean } = {}
-): Parameter[] {
- if (typeof schema === 'boolean') {
- return [];
- }
-
- switch (schema.type) {
- case 'string':
- case 'number':
- case 'boolean':
- case 'integer':
- case 'null':
- return [
- {
- name,
- type: schema.type,
- description: schema.description,
- array: options.array,
- required: options.required,
- constant: schema.const,
- enum: schema.enum !== undefined ? castArray(schema.enum) : schema.enum,
- },
- ];
-
- case 'array':
- if (
- typeof schema.items === 'boolean' ||
- typeof schema.items === 'undefined' ||
- isArray(schema.items)
- ) {
- return [];
- }
- return jsonSchemaToFlatParameters(schema.items as JSONSchema, name, {
- ...options,
- array: true,
- });
-
- default:
- case 'object':
- if (typeof schema.properties === 'undefined') {
- return [];
- }
- return Object.entries(schema.properties).flatMap(([key, subSchema]) => {
- return jsonSchemaToFlatParameters(subSchema, name ? `${name}.${key}` : key, {
- ...options,
- required: schema.required && schema.required.includes(key) ? true : false,
- });
- });
- }
-}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json
index 90fde8a283d9e..ba2daf98c1155 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant/tsconfig.json
@@ -45,7 +45,6 @@
"@kbn/expect",
"@kbn/apm-synthtrace-client",
"@kbn/apm-synthtrace",
- "@kbn/safer-lodash-set",
"@kbn/cloud-plugin",
"@kbn/serverless"
],
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx
index 5e6508d197661..f54dc021938d5 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/components/chat/chat_body.tsx
@@ -36,12 +36,14 @@ import { useGenAIConnectors } from '../../hooks/use_genai_connectors';
import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base';
import { useLicense } from '../../hooks/use_license';
import { useObservabilityAIAssistantChatService } from '../../hooks/use_observability_ai_assistant_chat_service';
+import { useSimulatedFunctionCalling } from '../../hooks/use_simulated_function_calling';
import { ASSISTANT_SETUP_TITLE, EMPTY_CONVERSATION_TITLE, UPGRADE_LICENSE_TITLE } from '../../i18n';
import { PromptEditor } from '../prompt_editor/prompt_editor';
import { FlyoutPositionMode } from './chat_flyout';
import { ChatHeader } from './chat_header';
import { ChatTimeline } from './chat_timeline';
import { IncorrectLicensePanel } from './incorrect_license_panel';
+import { SimulatedFunctionCallingCallout } from './simulated_function_calling_callout';
import { WelcomeMessage } from './welcome_message';
const fullHeightClassName = css`
@@ -127,6 +129,8 @@ export function ChatBody({
const chatService = useObservabilityAIAssistantChatService();
+ const { simulatedFunctionCallingEnabled } = useSimulatedFunctionCalling();
+
const { conversation, messages, next, state, stop, saveTitle } = useConversation({
initialConversationId,
initialMessages,
@@ -383,6 +387,12 @@ export function ChatBody({
+ {simulatedFunctionCallingEnabled ? (
+
+
+
+ ) : null}
+
+
+
+
+
+
+
+ {i18n.translate('xpack.observabilityAiAssistant.simulatedFunctionCallingCalloutLabel', {
+ defaultMessage:
+ 'Simulated function calling is enabled. You might see degradated performance.',
+ })}
+
+
+
+
+ );
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_simulated_function_calling.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_simulated_function_calling.ts
new file mode 100644
index 0000000000000..4d441b03a3ddc
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/public/hooks/use_simulated_function_calling.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { aiAssistantSimulatedFunctionCalling } from '@kbn/observability-ai-assistant-plugin/public';
+import { useKibana } from './use_kibana';
+
+export function useSimulatedFunctionCalling() {
+ const {
+ services: { uiSettings },
+ } = useKibana();
+
+ const simulatedFunctionCallingEnabled = uiSettings.get(
+ aiAssistantSimulatedFunctionCalling,
+ false
+ );
+
+ return { simulatedFunctionCallingEnabled };
+}
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts
index 33e401bf11c7d..ac56661af69fb 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.test.ts
@@ -82,4 +82,16 @@ describe('correctCommonEsqlMistakes', () => {
`FROM logs-*\n| KEEP date, whatever\n| RENAME whatever AS forever\n| SORT forever DESC`
);
});
+
+ it(`escapes the column name if SORT uses an expression`, () => {
+ expectQuery(
+ 'FROM logs-* \n| STATS COUNT(*) by service.name\n| SORT COUNT(*) DESC',
+ 'FROM logs-*\n| STATS COUNT(*) by service.name\n| SORT `COUNT(*)` DESC'
+ );
+
+ expectQuery(
+ 'FROM logs-* \n| STATS COUNT(*) by service.name\n| SORT COUNT(*) DESC, @timestamp ASC',
+ 'FROM logs-*\n| STATS COUNT(*) by service.name\n| SORT `COUNT(*)` DESC, @timestamp ASC'
+ );
+ });
});
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts
index b0877dede3a84..8d65f06ba9f70 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/correct_common_esql_mistakes.ts
@@ -146,6 +146,27 @@ function verifyKeepColumns(
return `KEEP ${columnsInKeep.join(', ')}`;
}
+function escapeExpressionsInSort(sortCommand: string) {
+ const columnsInSort = split(sortCommand.replace(/^SORT\s*/, ''), ',')
+ .map((statement) => split(statement, '=')?.[0].trim())
+ .map((columnAndSortOrder) => {
+ let [, column, sortOrder = ''] = columnAndSortOrder.match(/^(.*?)\s*(ASC|DESC)?$/i) || [];
+ if (!column) {
+ return columnAndSortOrder;
+ }
+
+ if (sortOrder) sortOrder = ` ${sortOrder}`;
+
+ if (!column.match(/^[a-zA-Z0-9_\.@]+$/)) {
+ column = `\`${column}\``;
+ }
+
+ return `${column}${sortOrder}`;
+ });
+
+ return `SORT ${columnsInSort.join(', ')}`;
+}
+
export function correctCommonEsqlMistakes(content: string, log: Logger) {
return content.replaceAll(/```esql\n(.*?)\n```/gms, (_, query: string) => {
const commands = splitIntoCommands(query);
@@ -172,6 +193,10 @@ export function correctCommonEsqlMistakes(content: string, log: Logger) {
case 'KEEP':
formattedCommand = verifyKeepColumns(formattedCommand, commands.slice(index + 1));
break;
+
+ case 'SORT':
+ formattedCommand = escapeExpressionsInSort(formattedCommand);
+ break;
}
return formattedCommand;
});
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-abs.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-abs.txt
index add29ab4b88e6..5c436a9dfa0ce 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-abs.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-abs.txt
@@ -2,7 +2,7 @@ ABS
Syntax
Parameters
-n
+number
Numeric expression. If null, the function returns null.
DescriptionReturns the absolute value.Supported types
Examples
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-acos.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-acos.txt
index 5cccde6b4321d..d4befd0a17aa2 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-acos.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-acos.txt
@@ -2,10 +2,9 @@ ACOS
Syntax
Parameters
-n
-Numeric expression. If null, the function returns null.
-DescriptionReturns the arccosine of n as an
-angle, expressed in radians.Supported types
+number
+Number between -1 and 1. If null, the function returns null.
+DescriptionReturns the arccosine of n as an angle, expressed in radians.Supported types
Example
```esql
ROW a=.9
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-asin.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-asin.txt
index 5210583fba7ef..79abf14656d69 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-asin.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-asin.txt
@@ -2,11 +2,9 @@ ASIN
Syntax
Parameters
-n
-Numeric expression. If null, the function returns null.
-DescriptionReturns the
-arcsine
-of the input numeric expression as an angle, expressed in radians.Supported types
+number
+Number between -1 and 1. If null, the function returns null.
+DescriptionReturns the arcsine of the input numeric expression as an angle, expressed in radians.Supported types
Example
```esql
ROW a=.9
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan.txt
index 8360a44147a02..9c8f0a080a1fd 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan.txt
@@ -2,11 +2,9 @@ ATAN
Syntax
Parameters
-n
+number
Numeric expression. If null, the function returns null.
-DescriptionReturns the
-arctangent of the
-input numeric expression as an angle, expressed in radians.Supported types
+DescriptionReturns the arctangent of the input numeric expression as an angle, expressed in radians.Supported types
Example
```esql
ROW a=12.9
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan2.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan2.txt
index 7bf09faf50e41..9caa5eca10d50 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan2.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-atan2.txt
@@ -2,13 +2,11 @@ ATAN2
Syntax
Parameters
-y
-Numeric expression. If null, the function returns null.
-x
-Numeric expression. If null, the function returns null.
-DescriptionThe angle between the positive x-axis and
-the ray from the origin to the point (x , y) in the Cartesian plane, expressed
-in radians.Supported types
+y_coordinate
+y coordinate. If null, the function returns null.
+x_coordinate
+x coordinate. If null, the function returns null.
+DescriptionThe angle between the positive x-axis and the ray from the origin to the point (x , y) in the Cartesian plane, expressed in radians.Supported types
Example
```esql
ROW y=12.9, x=.6
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-avg.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-avg.txt
index ea5132bab11b4..5ee99389900ed 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-avg.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-avg.txt
@@ -1,11 +1,19 @@
AVG
Syntax
-AVG(column)
-column
-Numeric column. If null, the function returns null.
-DescriptionThe average of a numeric field.Supported typesThe result is always a double no matter the input type.Example
+AVG(expression)
+expression
+Numeric expression.
+DescriptionThe average of a numeric expression.Supported typesThe result is always a double no matter the input type.Examples
```esql
FROM employees
| STATS AVG(height)
```
+
+The expression can use inline functions. For example, to calculate the average
+over a multivalued column, first use MV_AVG to average the multiple values per
+row, and use the result with the AVG function:
+```esql
+FROM employees
+| STATS avg_salary_change = ROUND(AVG(MV_AVG(salary_change)), 10)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-auto_bucket.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-bucket.txt
similarity index 70%
rename from x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-auto_bucket.txt
rename to x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-bucket.txt
index a4c81c732d35a..f0ed22f2db166 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-auto_bucket.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-bucket.txt
@@ -1,10 +1,10 @@
-AUTO_BUCKET
+BUCKET
Syntax
-AUTO_BUCKET(field, buckets, from, to)
+BUCKET(expression, buckets, from, to)
Parameters
field
-Numeric or date column from which to derive buckets.
+Numeric or date expression from which to derive buckets.
buckets
Target number of buckets.
from
@@ -13,50 +13,50 @@ to
End of the range. Can be a number or a date expressed as a string.
DescriptionCreates human-friendly buckets and returns a value for each row that corresponds
to the resulting bucket the row falls into.Using a target number of buckets, a start of a range, and an end of a range,
-AUTO_BUCKET picks an appropriate bucket size to generate the target number of
+BUCKET picks an appropriate bucket size to generate the target number of
buckets or fewer. For example, asking for at most 20 buckets over a year results
in monthly buckets:
```esql
FROM employees
| WHERE hire_date >= "1985-01-01T00:00:00Z" AND hire_date < "1986-01-01T00:00:00Z"
-| EVAL month = AUTO_BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
+| EVAL month = BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
| KEEP hire_date, month
| SORT hire_date
```
The goal isn’t to provide exactly the target number of buckets, it’s to pick a
range that people are comfortable with that provides at most the target number
-of buckets.Combine AUTO_BUCKET with
+of buckets.Combine BUCKET with
STATS ... BY to create a histogram:
```esql
FROM employees
| WHERE hire_date >= "1985-01-01T00:00:00Z" AND hire_date < "1986-01-01T00:00:00Z"
-| EVAL month = AUTO_BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
+| EVAL month = BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
| STATS hires_per_month = COUNT(*) BY month
| SORT month
```
-AUTO_BUCKET does not create buckets that don’t match any documents.
+BUCKET does not create buckets that don’t match any documents.
That’s why this example is missing 1985-03-01 and other dates.
Asking for more buckets can result in a smaller range. For example, asking for
at most 100 buckets in a year results in weekly buckets:
```esql
FROM employees
| WHERE hire_date >= "1985-01-01T00:00:00Z" AND hire_date < "1986-01-01T00:00:00Z"
-| EVAL week = AUTO_BUCKET(hire_date, 100, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
+| EVAL week = BUCKET(hire_date, 100, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
| STATS hires_per_week = COUNT(*) BY week
| SORT week
```
-AUTO_BUCKET does not filter any rows. It only uses the provided range to
+BUCKET does not filter any rows. It only uses the provided range to
pick a good bucket size. For rows with a value outside of the range, it returns
a bucket value that corresponds to a bucket outside the range. Combine
-AUTO_BUCKET with WHERE to filter rows.
-AUTO_BUCKET can also operate on numeric fields. For example, to create a
+BUCKET with WHERE to filter rows.
+BUCKET can also operate on numeric fields. For example, to create a
salary histogram:
```esql
FROM employees
-| EVAL bs = AUTO_BUCKET(salary, 20, 25324, 74999)
+| EVAL bs = BUCKET(salary, 20, 25324, 74999)
| STATS COUNT(*) by bs
| SORT bs
```
@@ -68,7 +68,7 @@ per hour:
```esql
FROM sample_data
| WHERE @timestamp >= NOW() - 1 day and @timestamp < NOW()
-| EVAL bucket = AUTO_BUCKET(@timestamp, 25, DATE_FORMAT(NOW() - 1 day), DATE_FORMAT(NOW()))
+| EVAL bucket = BUCKET(@timestamp, 25, NOW() - 1 day, NOW())
| STATS COUNT(*) BY bucket
```
@@ -77,7 +77,7 @@ hiring month:
```esql
FROM employees
| WHERE hire_date >= "1985-01-01T00:00:00Z" AND hire_date < "1986-01-01T00:00:00Z"
-| EVAL bucket = AUTO_BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
+| EVAL bucket = BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
| STATS AVG(salary) BY bucket
| SORT bucket
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ceil.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ceil.txt
index 8fac3b0bddc2f..d8bea9d574ea1 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ceil.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ceil.txt
@@ -2,12 +2,10 @@ CEIL
Syntax
Parameters
-n
+number
Numeric expression. If null, the function returns null.
DescriptionRound a number up to the nearest integer.
-This is a noop for long (including unsigned) and integer.
- For double this picks the closest double value to the integer
- similar to Math.ceil.
+This is a noop for long (including unsigned) and integer. For double this picks the closest double value to the integer similar to Math.ceil.
Supported types
Example
```esql
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-coalesce.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-coalesce.txt
index b8d3d2498b57b..87e4de6189078 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-coalesce.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-coalesce.txt
@@ -3,10 +3,11 @@ COALESCE
Syntax
COALESCE(expression1 [, ..., expressionN])
Parameters
-expressionX
-Expression to evaluate.
-DescriptionReturns the first of its arguments that is not null. If all arguments are null,
-it returns null.Example
+first
+Expression to evaluate
+rest
+Other expression to evaluate
+DescriptionReturns the first of its arguments that is not null. If all arguments are null, it returns null.Example
```esql
ROW a=null, b="b"
| EVAL COALESCE(a, b)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cos.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cos.txt
index ebda0596fca8a..09154626e1853 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cos.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cos.txt
@@ -2,10 +2,9 @@ COS
Syntax
Parameters
-n
-Numeric expression. If null, the function returns null.
-DescriptionReturns the cosine of n. Input
-expected in radians.Supported types
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns the cosine of an angle.Supported types
Example
```esql
ROW a=1.8
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cosh.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cosh.txt
index a8920bdf4e694..5fa95d2a204b4 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cosh.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-cosh.txt
@@ -2,11 +2,10 @@ COSH
Syntax
Parameters
-n
-Numeric expression. If null, the function returns null.
-Supported types
-DescriptionReturns the hyperbolic
-cosine.Example
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns the hyperbolic cosine of an angle.Supported types
+Example
```esql
ROW a=1.8
| EVAL cosh=COSH(a)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count.txt
index c3c7136459ebb..326d5c2e1caae 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count.txt
@@ -1,11 +1,11 @@
COUNT
Syntax
-COUNT([input])
+COUNT([expression])
Parameters
-input
-Column or literal for which to count the number of values. If omitted, returns a
-count all (the number of rows).
+expression
+Expression that outputs values to be counted.
+If omitted, equivalent to COUNT(*) (the number of rows).
DescriptionReturns the total number (count) of input values.Supported typesCan take any field type as input.Examples
```esql
FROM employees
@@ -18,3 +18,10 @@ FROM employees
| STATS count = COUNT(*) BY languages
| SORT languages DESC
```
+
+The expression can use inline functions. This example splits a string into
+multiple values using the SPLIT function and counts the values:
+```esql
+ROW words="foo;bar;baz;qux;quux;foo"
+| STATS word_count = COUNT(SPLIT(words, ";"))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count_distinct.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count_distinct.txt
index b09825440dc42..6d4abccfb884a 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count_distinct.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-count_distinct.txt
@@ -1,13 +1,34 @@
COUNT_DISTINCT
Syntax
-COUNT_DISTINCT(column[, precision])
+COUNT_DISTINCT(expression[, precision_threshold])
Parameters
-column
-Column for which to count the number of distinct values.
-precision
-Precision. Refer to Counts are approximate.
-DescriptionReturns the approximate number of distinct values.Counts are approximateeditComputing exact counts requires loading values into a set and returning its
+expression
+Expression that outputs the values on which to perform a distinct count.
+precision_threshold
+Precision threshold. Refer to Counts are approximate. The
+maximum supported value is 40000. Thresholds above this number will have the
+same effect as a threshold of 40000. The default value is 3000.
+DescriptionReturns the approximate number of distinct values.Supported typesCan take any field type as input.Examples
+```esql
+FROM hosts
+| STATS COUNT_DISTINCT(ip0), COUNT_DISTINCT(ip1)
+```
+
+With the optional second parameter to configure the precision threshold:
+```esql
+FROM hosts
+| STATS COUNT_DISTINCT(ip0, 80000), COUNT_DISTINCT(ip1, 5)
+```
+
+The expression can use inline functions. This example splits a string into
+multiple values using the SPLIT function and counts the unique values:
+```esql
+ROW words="foo;bar;baz;qux;quux;foo"
+| STATS distinct_word_count = COUNT_DISTINCT(SPLIT(words, ";"))
+```
+
+Counts are approximateeditComputing exact counts requires loading values into a set and returning its
size. This doesn’t scale when working on high-cardinality sets and/or large
values as the required memory usage and the need to communicate those
per-shard sets between nodes would utilize too many resources of the cluster.This COUNT_DISTINCT function is based on the
@@ -25,15 +46,9 @@ on the dataset in question. In general, most datasets show consistently good
accuracy. Also note that even with a threshold as low as 100, the error
remains very low (1-6% as seen in the above graph) even when counting millions of items.The HyperLogLog++ algorithm depends on the leading zeros of hashed
values, the exact distributions of hashes in a dataset can affect the
-accuracy of the cardinality.The COUNT_DISTINCT function takes an optional second parameter to configure the
-precision.Supported typesCan take any field type as input.Examples
-```esql
-FROM hosts
-| STATS COUNT_DISTINCT(ip0), COUNT_DISTINCT(ip1)
-```
-
-With the optional second parameter to configure the precision:
-```esql
-FROM hosts
-| STATS COUNT_DISTINCT(ip0, 80000), COUNT_DISTINCT(ip1, 5)
-```
+accuracy of the cardinality.The COUNT_DISTINCT function takes an optional second parameter to configure
+the precision threshold. The precision_threshold options allows to trade memory
+for accuracy, and defines a unique count below which counts are expected to be
+close to accurate. Above this value, counts might become a bit more fuzzy. The
+maximum supported value is 40000, thresholds above this number will have the
+same effect as a threshold of 40000. The default value is 3000.
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_diff.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_diff.txt
new file mode 100644
index 0000000000000..1d9de78792c1a
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_diff.txt
@@ -0,0 +1,19 @@
+DATE_DIFF
+
+Syntax
+Parameters
+unit
+Time difference unit.
+startTimestamp
+Start timestamp.
+endTimestamp
+End timestamp.
+DescriptionSubtracts the startTimestamp from the endTimestamp and returns the
+difference in multiples of unit. If startTimestamp is later than the
+endTimestamp, negative values are returned.
+Supported types
+Example
+```esql
+ROW date1 = TO_DATETIME("2023-12-02T11:00:00.000Z"), date2 = TO_DATETIME("2023-12-02T11:00:00.001Z")
+| EVAL dd_ms = DATE_DIFF("microseconds", date1, date2)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_trunc.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_trunc.txt
index dd87fa74a69d4..073ac87c7ab3b 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_trunc.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-date_trunc.txt
@@ -1,14 +1,13 @@
DATE_TRUNC
Syntax
-DATE_TRUNC(interval, date)
Parameters
interval
-Interval, expressed using the timespan literal
-syntax. If null, the function returns null.
+Interval; expressed using the timespan literal syntax.
date
-Date expression. If null, the function returns null.
-DescriptionRounds down a date to the closest interval.Examples
+Date expression
+DescriptionRounds down a date to the closest interval.Supported types
+Examples
```esql
FROM employees
| KEEP first_name, last_name, hire_date
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-e.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-e.txt
index 4438b7dc35683..48a4adf1d3186 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-e.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-e.txt
@@ -1,7 +1,8 @@
E
-
-Euler’s number.
+Syntax
+ParametersDescriptionReturns Euler’s number.Supported types
+Example
```esql
ROW E()
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-enrich.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-enrich.txt
index c7de9fd3e3c56..39b03a08f1c3d 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-enrich.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-enrich.txt
@@ -6,6 +6,9 @@ Parameters
policy
The name of the enrich policy. You need to create
and execute the enrich policy first.
+mode
+The mode of the enrich command in cross cluster ES|QL.
+See enrich across clusters.
match_field
The match field. ENRICH uses its value to look for records in the enrich
index. If not specified, the match will be performed on the column with the same
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-eval.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-eval.txt
index fb11bfe545510..991fae551af1e 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-eval.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-eval.txt
@@ -1,7 +1,7 @@
EVAL
Syntax
-EVAL column1 = value1[, ..., columnN = valueN]
+EVAL [column1 =] value1[, ..., [columnN =] valueN]
Parameters
columnX
The column name.
@@ -26,3 +26,21 @@ FROM employees
| KEEP first_name, last_name, height
| EVAL height = height * 3.281
```
+
+Specifying the output column name is optional. If not specified, the new column
+name is equal to the expression. The following query adds a column named
+height*3.281:
+```esql
+FROM employees
+| SORT emp_no
+| KEEP first_name, last_name, height
+| EVAL height * 3.281
+```
+
+Because this name contains special characters, it needs to be
+quoted with backticks (`) when using it in subsequent commands:
+```esql
+FROM employees
+| EVAL height * 3.281
+| STATS avg_height_feet = AVG(`height * 3.281`)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-floor.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-floor.txt
index cb68b3277cdc3..d994fd9439fc4 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-floor.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-floor.txt
@@ -1,13 +1,16 @@
FLOOR
-
-Round a number down to the nearest integer.
+Syntax
+Parameters
+number
+Numeric expression. If null, the function returns null.
+DescriptionRound a number down to the nearest integer.
+This is a noop for long (including unsigned) and integer.
+For double this picks the closest double value to the integer
+similar to Math.floor.
+Supported types
+Example
```esql
ROW a=1.8
| EVAL a=FLOOR(a)
```
-
-This is a noop for long (including unsigned) and integer.
- For double this picks the the closest double value to the integer ala
- Math.floor.
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-from.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-from.txt
index f57f10de37c96..fa1226470e38e 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-from.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-from.txt
@@ -2,7 +2,7 @@ FROM
Syntax
```esql
-FROM index_pattern [METADATA fields]
+FROM index_pattern [METADATA fields] [OPTIONS options]
```
Parameters
@@ -10,6 +10,9 @@ index_pattern
A list of indices, data streams or aliases. Supports wildcards and date math.
fields
A comma-separated list of metadata fields to retrieve.
+options
+A comma-separated list of index options to configure
+data access.
DescriptionThe
```esql
FROM source command returns a table with data from a data stream, index,
@@ -18,7 +21,7 @@ FROM source command returns a table with data from a data stream, index,
or alias. Each row in the resulting table represents a document. Each column
corresponds to a field, and can be accessed by the name of that field.
By default, an ES|QL query without an explicit LIMIT uses an implicit
-limit of 500. This applies to
+limit of 1000. This applies to
```esql
FROM too. A FROM command without LIMIT:
```
@@ -30,7 +33,7 @@ FROM employees
is executed as:
```esql
FROM employees
-| LIMIT 500
+| LIMIT 1000
```
Examples
@@ -51,7 +54,19 @@ or aliases:
FROM employees-00001,other-employees-*
```
-Use the METADATA directive to enable metadata fields:
+Use the format : to query data streams and indices
+on remote clusters:
```esql
-FROM employees [METADATA _id]
+FROM cluster_one:employees-00001,cluster_two:other-employees-*
+```
+
+See using ES|QL across clusters.Use the optional METADATA directive to enable metadata fields:
+```esql
+FROM employees METADATA _id
+```
+
+Use the optional OPTIONS directive to specify index access options.
+This directive must follow METADATA, if both are specified:
+```esql
+FROM employees* METADATA _index OPTIONS "ignore_unavailable"="true"
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-functions-overview.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-functions-overview.txt
new file mode 100644
index 0000000000000..0953f57df5587
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-functions-overview.txt
@@ -0,0 +1,118 @@
+Functions overview
+
+
+Aggregate functions
+AVG
+COUNT
+COUNT_DISTINCT
+MAX
+MEDIAN
+MEDIAN_ABSOLUTE_DEVIATION
+MIN
+PERCENTILE
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_CENTROID_AGG
+SUM
+VALUES
+Math functions
+ABS
+ACOS
+ASIN
+ATAN
+ATAN2
+CEIL
+COS
+COSH
+E
+FLOOR
+LOG
+LOG10
+PI
+POW
+ROUND
+SIGNUM
+SIN
+SINH
+SQRT
+TAN
+TANH
+TAU
+String functions
+CONCAT
+LEFT
+LENGTH
+LOCATE
+LTRIM
+REPLACE
+RIGHT
+RTRIM
+SPLIT
+SUBSTRING
+TO_LOWER
+TO_UPPER
+TRIM
+Date and time functions
+BUCKET
+DATE_DIFF
+DATE_EXTRACT
+DATE_FORMAT
+DATE_PARSE
+DATE_TRUNC
+NOW
+Spatial functions
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_INTERSECTS
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_DISJOINT
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_CONTAINS
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_WITHIN
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_X
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_Y
+Type conversion functions
+TO_BOOLEAN
+TO_CARTESIANPOINT
+TO_CARTESIANSHAPE
+TO_DATETIME
+TO_DEGREES
+TO_DOUBLE
+TO_GEOPOINT
+TO_GEOSHAPE
+TO_INTEGER
+TO_IP
+TO_LONG
+TO_RADIANS
+TO_STRING
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+TO_UNSIGNED_LONG
+TO_VERSION
+Conditional functions and expressions
+CASE
+COALESCE
+GREATEST
+LEAST
+Multi value functions
+MV_AVG
+MV_CONCAT
+MV_COUNT
+MV_DEDUPE
+MV_FIRST
+MV_LAST
+MV_MAX
+MV_MEDIAN
+MV_MIN
+MV_SORT
+MV_SLICE
+MV_SUM
+MV_ZIP
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-greatest.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-greatest.txt
index 3a217abe2a0e4..740bb14a7c141 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-greatest.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-greatest.txt
@@ -1,14 +1,19 @@
GREATEST
-
-Returns the maximum value from many columns. This is similar to MV_MAX
-except it’s intended to run on multiple columns at once.
+Syntax
+Parameters
+first
+First of the columns to evaluate.
+rest
+The rest of the columns to evaluate.
+DescriptionReturns the maximum value from multiple columns. This is similar to MV_MAX
+except it is intended to run on multiple columns at once.
+When run on keyword or text fields, this returns the last string
+ in alphabetical order. When run on boolean columns this will return
+ true if any values are true.
+Supported types
+Example
```esql
ROW a = 10, b = 20
| EVAL g = GREATEST(a, b)
```
-
-When run on keyword or text fields, this’ll return the last string
- in alphabetical order. When run on boolean columns this will return
- true if any values are true.
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-keep.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-keep.txt
index 287e079bb861c..d1a3753abb26e 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-keep.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-keep.txt
@@ -3,9 +3,15 @@ KEEP
Syntax
KEEP columns
Parameters
-columns::
-A comma-separated list of columns to keep. Supports wildcards.DescriptionThe KEEP processing command enables you to specify what columns are returned
-and the order in which they are returned.ExamplesThe columns are returned in the specified order:
+columns
+A comma-separated list of columns to keep. Supports wildcards.
+DescriptionThe KEEP processing command enables you to specify what columns are returned
+and the order in which they are returned.Precedence rules are applied when a field name matches multiple expressions.
+Fields are added in the order they appear. If one field matches multiple expressions, the following precedence rules apply (from highest to lowest priority):
+Complete field name (no wildcards)
+Partial wildcard expressions (for example: fieldNam*)
+Wildcard only (*)
+If a field matches two expressions with the same precedence, the right-most expression wins.Refer to the examples for illustrations of these precedence rules.ExamplesThe columns are returned in the specified order:
```esql
FROM employees
| KEEP emp_no, first_name, last_name, height
@@ -19,9 +25,28 @@ FROM employees
```
The asterisk wildcard (*) by itself translates to all columns that do not
-match the other arguments. This query will first return all columns with a name
+match the other arguments.This query will first return all columns with a name
that starts with h, followed by all other columns:
```esql
FROM employees
| KEEP h*, *
```
+
+The following examples show how precedence rules work when a field name matches multiple expressions.Complete field name has precedence over wildcard expressions:
+```esql
+FROM employees
+| KEEP first_name, last_name, first_name*
+```
+
+Wildcard expressions have the same priority, but last one wins (despite being less specific):
+```esql
+FROM employees
+| KEEP first_name*, last_name, first_na*
+```
+
+A simple wildcard expression * has the lowest precedence.
+Output order is determined by the other arguments:
+```esql
+FROM employees
+| KEEP *, first_name
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-least.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-least.txt
index 949864ec07e4f..ee76f5a43ac0d 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-least.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-least.txt
@@ -1,14 +1,19 @@
LEAST
-
-Returns the minimum value from many columns. This is similar to MV_MIN
-except it’s intended to run on multiple columns at once.
+Syntax
+Parameters
+first
+First of the columns to evaluate.
+rest
+The rest of the columns to evaluate.
+DescriptionReturns the minimum value from multiple columns. This is similar to
+MV_MIN except it is intended to run on multiple columns at once.
+When run on keyword or text fields, this returns the first string
+ in alphabetical order. When run on boolean columns this will return
+ false if any values are false.
+Supported types
+Example
```esql
ROW a = 10, b = 20
| EVAL l = LEAST(a, b)
```
-
-When run on keyword or text fields, this’ll return the first string
- in alphabetical order. When run on boolean columns this will return
- false if any values are false.
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-left.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-left.txt
index fea60895db137..1aae39f111122 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-left.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-left.txt
@@ -1,7 +1,13 @@
LEFT
-
-Return the substring that extracts length chars from the string starting from the left.
+Syntax
+Parameters
+string
+The string from which to return a substring.
+length
+The number of characters to return.
+DescriptionReturns the substring that extracts length chars from string starting from the left.Supported types
+Example
```esql
FROM employees
| KEEP last_name
@@ -9,5 +15,3 @@ FROM employees
| SORT last_name ASC
| LIMIT 5
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-length.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-length.txt
index b8a2ecf821e64..83d15509bfa67 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-length.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-length.txt
@@ -1,8 +1,13 @@
LENGTH
-Returns the character length of a string.
+Syntax
+LENGTH(str)
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionReturns the character length of a string.Example
```esql
FROM employees
-| KEEP first_name, last_name, height
+| KEEP first_name, last_name
| EVAL fn_length = LENGTH(first_name)
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-limitations.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-limitations.txt
index c5ae151eab121..3b0bb15b02c15 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-limitations.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-limitations.txt
@@ -9,7 +9,7 @@ Elastic Docs
ES|QL examples »
ES|QL limitationsedit
Result set size limitedit
-By default, an ES|QL query returns up to 500 rows. You can increase the number
+By default, an ES|QL query returns up to 1000 rows. You can increase the number
of rows up to 10,000 using the `LIMIT` command.
Queries do not return more than 10,000 rows, regardless of the `LIMIT` command’s
value.
@@ -37,20 +37,21 @@ ES|QL currently supports the following field types:
`long`
`null`
`text`
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
`unsigned_long`
`version`
Spatial types
`geo_point`
+`geo_shape`
`point`
+`shape`
Unsupported typesedit
ES|QL does not yet support the following field types:
TSDB metrics
`counter`
`position`
`aggregate_metric_double`
-Spatial types
-`geo_shape`
-`shape`
Date/time
`date_nanos`
`date_range`
@@ -73,6 +74,21 @@ Querying a column with an unsupported type returns an error. If a column with an
unsupported type is not explicitly used in a query, it is returned with `null`
values, with the exception of nested fields. Nested fields are not returned at
all.
+Limitations on supported typesedit
+Some field types are not supported in all contexts:
+Spatial types are not supported in the SORT processing command.
+Specifying a column of one of these types as a sort parameter will result in an error:
+`geo_point`
+`geo_shape`
+`cartesian_point`
+`cartesian_shape`
+_source availabilityedit
+ES|QL does not support configurations where the
+_source field is disabled.
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ES|QL’s support for synthetic `_source`
+is currently experimental.
Full-text search is not supportededit
Because of the way ES|QL treats `text` values,
full-text search is not yet supported. Queries on `text` fields are like queries
@@ -104,8 +120,6 @@ To avoid these issues, a best practice is to be explicit about the field that
you query, and query `keyword` sub-fields instead of `text` fields.
Time series data streams are not supportededit
ES|QL does not support querying time series data streams (TSDS).
-Cross-cluster search is not supportededit
-ES|QL does not support cross-cluster search.
Date math limitationsedit
Date math expressions work well when the leftmost expression is a datetime, for
example:
@@ -142,7 +156,10 @@ of rows that are retrieved by the query and displayed in Discover. Queries and
aggregations run on the full data set.
Discover shows no more than 50 columns. If a query returns
more than 50 columns, Discover only shows the first 50.
-Querying many many indices at once without any filters can cause an error in
+CSV export from Discover shows no more than 10,000 rows. This limit only applies to the number
+of rows that are retrieved by the query and displayed in Discover. Queries and
+aggregations run on the full data set.
+Querying many indices at once without any filters can cause an error in
kibana which looks like `[esql] > Unexpected error from Elasticsearch: The
content length (536885793) is bigger than the maximum allowed string
(536870888)`. The response from ES|QL is too long. Use `DROP` or
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-locate.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-locate.txt
new file mode 100644
index 0000000000000..9059a5e424a8f
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-locate.txt
@@ -0,0 +1,11 @@
+LOCATE
+
+Syntax
+Parameters
+string
+An input string
+substring
+A substring to locate in the input string
+start
+The start index
+DescriptionReturns an integer that indicates the position of a keyword substring within another stringSupported types
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log.txt
new file mode 100644
index 0000000000000..c3e97b9fe5fcf
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log.txt
@@ -0,0 +1,17 @@
+LOG
+
+Syntax
+Parameters
+base
+Base of logarithm. If null, the function returns null. If not provided, this function returns the natural logarithm (base e) of a value.
+number
+Numeric expression. If null, the function returns null.
+DescriptionReturns the logarithm of a value to a base. The input can be any numeric value, the return value is always a double. Logs of zero, negative numbers, and base of one return null as well as a warning.Supported types
+Examples
+```esql
+ROW base = 2.0, value = 8.0
+| EVAL s = LOG(base, value)
+```
+
+row value = 100
+| EVAL s = LOG(value);
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log10.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log10.txt
index 259ce71384a36..5fbe4a94cd683 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log10.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-log10.txt
@@ -1,11 +1,12 @@
LOG10
-
-Returns the log base 10. The input can be any numeric value, the return value
-is always a double.Logs of negative numbers are NaN. Logs of infinites are infinite, as is the log of 0.
+Syntax
+Parameters
+number
+Numeric expression. If null, the function returns null.
+DescriptionReturns the logarithm of a value to base 10. The input can be any numeric value, the return value is always a double. Logs of 0 and negative numbers return null as well as a warning.Supported types
+Example
```esql
ROW d = 1000.0
| EVAL s = LOG10(d)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ltrim.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ltrim.txt
index d4d8e1c244a51..b08fd33a54823 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ltrim.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-ltrim.txt
@@ -1,7 +1,11 @@
LTRIM
-
-Removes leading whitespaces from strings.
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionRemoves leading whitespaces from strings.Supported types
+Example
```esql
ROW message = " some text ", color = " red "
| EVAL message = LTRIM(message)
@@ -9,5 +13,3 @@ ROW message = " some text ", color = " red "
| EVAL message = CONCAT("'", message, "'")
| EVAL color = CONCAT("'", color, "'")
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-max.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-max.txt
index eae40918c20eb..fe4522d77d692 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-max.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-max.txt
@@ -1,7 +1,20 @@
MAX
-The maximum value of a numeric field.
+Syntax
+MAX(expression)
+Parameters
+expression
+Expression from which to return the maximum value.
+DescriptionReturns the maximum value of a numeric expression.Example
```esql
FROM employees
| STATS MAX(languages)
```
+
+The expression can use inline functions. For example, to calculate the maximum
+over an average of a multivalued column, use MV_AVG to first average the
+multiple values per row, and use the result with the MAX function:
+```esql
+FROM employees
+| STATS max_avg_salary_change = MAX(MV_AVG(salary_change))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median.txt
index 84807da7a352d..6f365afd34aec 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median.txt
@@ -1,12 +1,25 @@
MEDIAN
-The value that is greater than half of all values and less than half of
+Syntax
+MEDIAN(expression)
+Parameters
+expression
+Expression from which to return the median value.
+DescriptionReturns the value that is greater than half of all values and less than half of
all values, also known as the 50% PERCENTILE.
+Like PERCENTILE, MEDIAN is usually approximate.
+MEDIAN is also non-deterministic.
+This means you can get slightly different results using the same data.
+Example
```esql
FROM employees
| STATS MEDIAN(salary), PERCENTILE(salary, 50)
```
-Like PERCENTILE, MEDIAN is usually approximate.
-MEDIAN is also non-deterministic.
-This means you can get slightly different results using the same data.
+The expression can use inline functions. For example, to calculate the median of
+the maximum values of a multivalued column, first use MV_MAX to get the
+maximum value per row, and use the result with the MEDIAN function:
+```esql
+FROM employees
+| STATS median_max_salary_change = MEDIAN(MV_MAX(salary_change))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median_absolute_deviation.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median_absolute_deviation.txt
index f05b04d62e497..0098c9ca8f3eb 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median_absolute_deviation.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-median_absolute_deviation.txt
@@ -1,17 +1,31 @@
MEDIAN_ABSOLUTE_DEVIATION
-The median absolute deviation, a measure of variability. It is a robust
+Syntax
+MEDIAN_ABSOLUTE_DEVIATION(expression)
+Parameters
+expression
+Expression from which to return the median absolute deviation.
+DescriptionReturns the median absolute deviation, a measure of variability. It is a robust
statistic, meaning that it is useful for describing data that may have outliers,
-or may not be normally distributed. For such data it can be more descriptive than
-standard deviation.It is calculated as the median of each data point’s deviation from the median of
-the entire sample. That is, for a random variable X, the median absolute deviation
-is median(|median(X) - Xi|).
+or may not be normally distributed. For such data it can be more descriptive
+than standard deviation.It is calculated as the median of each data point’s deviation from the median of
+the entire sample. That is, for a random variable X, the median absolute
+deviation is median(|median(X) - X|).
+Like PERCENTILE, MEDIAN_ABSOLUTE_DEVIATION is
+ usually approximate.
+MEDIAN_ABSOLUTE_DEVIATION is also non-deterministic.
+This means you can get slightly different results using the same data.
+Example
```esql
FROM employees
| STATS MEDIAN(salary), MEDIAN_ABSOLUTE_DEVIATION(salary)
```
-Like PERCENTILE, MEDIAN_ABSOLUTE_DEVIATION is
- usually approximate.
-MEDIAN_ABSOLUTE_DEVIATION is also non-deterministic.
-This means you can get slightly different results using the same data.
+The expression can use inline functions. For example, to calculate the the
+median absolute deviation of the maximum values of a multivalued column, first
+use MV_MAX to get the maximum value per row, and use the result with the
+MEDIAN_ABSOLUTE_DEVIATION function:
+```esql
+FROM employees
+| STATS m_a_d_max_salary_change = MEDIAN_ABSOLUTE_DEVIATION(MV_MAX(salary_change))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-min.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-min.txt
index 62ff3d7c6be86..7b205f00e0784 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-min.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-min.txt
@@ -1,7 +1,20 @@
MIN
-The minimum value of a numeric field.
+Syntax
+MIN(expression)
+Parameters
+expression
+Expression from which to return the minimum value.
+DescriptionReturns the minimum value of a numeric expression.Example
```esql
FROM employees
| STATS MIN(languages)
```
+
+The expression can use inline functions. For example, to calculate the minimum
+over an average of a multivalued column, use MV_AVG to first average the
+multiple values per row, and use the result with the MIN function:
+```esql
+FROM employees
+| STATS min_avg_salary_change = MIN(MV_AVG(salary_change))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_avg.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_avg.txt
index 554e0a50c7a59..da2e084d4ade7 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_avg.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_avg.txt
@@ -1,10 +1,14 @@
MV_AVG
-Converts a multivalued field into a single valued field containing the average
-of all of the values. For example:
+Syntax
+MV_AVG(expression)
+Parameters
+expression
+Multivalue expression.
+DescriptionConverts a multivalued expression into a single valued column containing the
+average of all of the values.Supported types
+Example
```esql
ROW a=[3, 5, 1, 6]
| EVAL avg_a = MV_AVG(a)
```
-
-The output type is always a double and the input type can be any number.
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_concat.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_concat.txt
index 872234370fddb..e918a53d1dcc1 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_concat.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_concat.txt
@@ -1,17 +1,21 @@
MV_CONCAT
-
-Converts a multivalued string field into a single valued field containing the
-concatenation of all values separated by a delimiter:
+Syntax
+Parameters
+v
+Multivalue expression.
+delim
+Delimiter.
+DescriptionConverts a multivalued string expression into a single valued column containing
+the concatenation of all values separated by a delimiter.Supported types
+Examples
```esql
ROW a=["foo", "zoo", "bar"]
| EVAL j = MV_CONCAT(a, ", ")
```
-If you want to concat non-string fields call TO_STRING on them first:
+To concat non-string columns, call TO_STRING first:
```esql
ROW a=[10, 9, 8]
| EVAL j = MV_CONCAT(TO_STRING(a), ", ")
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_count.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_count.txt
index e2e1884e76604..bfda217135ad2 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_count.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_count.txt
@@ -1,11 +1,13 @@
MV_COUNT
-
-Converts a multivalued field into a single valued field containing a count of the number
-of values:
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued expression into a single valued column containing a count
+of the number of values.Supported types
+Example
```esql
ROW a=["foo", "zoo", "bar"]
| EVAL count_a = MV_COUNT(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_dedupe.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_dedupe.txt
index b5335d48506db..2a38a0a36480e 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_dedupe.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_dedupe.txt
@@ -1,11 +1,14 @@
MV_DEDUPE
-
-Removes duplicates from a multivalued field. For example:
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionRemoves duplicates from a multivalue expression.
+MV_DEDUPE may, but won’t always, sort the values in the column.
+Supported types
+Example
```esql
ROW a=["foo", "foo", "bar", "foo"]
| EVAL dedupe_a = MV_DEDUPE(a)
```
-
-Supported types:
-MV_DEDUPE may, but won’t always, sort the values in the field.
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_first.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_first.txt
new file mode 100644
index 0000000000000..e1dee9c3d9e73
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_first.txt
@@ -0,0 +1,18 @@
+MV_FIRST
+
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued expression into a single valued column containing the
+first value. This is most useful when reading from a function that emits
+multivalued columns in a known order like SPLIT.The order that multivalued fields are read from
+underlying storage is not guaranteed. It is frequently ascending, but don’t
+rely on that. If you need the minimum value use MV_MIN instead of
+MV_FIRST. MV_MIN has optimizations for sorted values so there isn’t a
+performance benefit to MV_FIRST.Supported types
+Example
+```esql
+ROW a="foo;bar;baz"
+| EVAL first_a = MV_FIRST(SPLIT(a, ";"))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_last.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_last.txt
new file mode 100644
index 0000000000000..996316e39b215
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_last.txt
@@ -0,0 +1,18 @@
+MV_LAST
+
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalue expression into a single valued column containing the last
+value. This is most useful when reading from a function that emits multivalued
+columns in a known order like SPLIT.The order that multivalued fields are read from
+underlying storage is not guaranteed. It is frequently ascending, but don’t
+rely on that. If you need the maximum value use MV_MAX instead of
+MV_LAST. MV_MAX has optimizations for sorted values so there isn’t a
+performance benefit to MV_LAST.Supported types
+Example
+```esql
+ROW a="foo;bar;baz"
+| EVAL last_a = MV_LAST(SPLIT(a, ";"))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_max.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_max.txt
index 430f23b27c43a..b5cd928c19522 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_max.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_max.txt
@@ -1,17 +1,20 @@
MV_MAX
-
-Converts a multivalued field into a single valued field containing the maximum value. For example:
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued expression into a single valued column containing the
+maximum value.Supported types
+Examples
```esql
ROW a=[3, 5, 1]
| EVAL max_a = MV_MAX(a)
```
-It can be used by any field type, including keyword fields. In that case picks the
-last string, comparing their utf-8 representation byte by byte:
+It can be used by any column type, including keyword columns. In that case
+it picks the last string, comparing their utf-8 representation byte by byte:
```esql
ROW a=["foo", "zoo", "bar"]
| EVAL max_a = MV_MAX(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_median.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_median.txt
index 63ab2329d9ebb..6e135c2dc1807 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_median.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_median.txt
@@ -1,14 +1,21 @@
MV_MEDIAN
-Converts a multivalued field into a single valued field containing the median value. For example:
+
+MV_MEDIAN(v)
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued column into a single valued column containing the median
+value.Supported types
+Examples
```esql
ROW a=[3, 5, 1]
| EVAL median_a = MV_MEDIAN(a)
```
-It can be used by any numeric field type and returns a value of the same type. If the
-row has an even number of values for a column the result will be the average of the
-middle two entries. If the field is not floating point then the average rounds down:
+If the row has an even number of values for a column, the result will be the
+average of the middle two entries. If the column is not floating point, the
+average rounds down:
```esql
ROW a=[3, 7, 1, 6]
| EVAL median_a = MV_MEDIAN(a)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_min.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_min.txt
index 1179f61d280d6..8d91fb50568a1 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_min.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_min.txt
@@ -1,17 +1,20 @@
MV_MIN
-
-Converts a multivalued field into a single valued field containing the minimum value. For example:
+Syntax
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued expression into a single valued column containing the
+minimum value.Supported types
+Examples
```esql
ROW a=[2, 1]
| EVAL min_a = MV_MIN(a)
```
-It can be used by any field type, including keyword fields. In that case picks the
-first string, comparing their utf-8 representation byte by byte:
+It can be used by any column type, including keyword columns. In that case,
+it picks the first string, comparing their utf-8 representation byte by byte:
```esql
ROW a=["foo", "bar"]
| EVAL min_a = MV_MIN(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_slice.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_slice.txt
new file mode 100644
index 0000000000000..30c6dffe3f764
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_slice.txt
@@ -0,0 +1,16 @@
+MV_SLICE
+
+Syntax
+Parameters
+field
+Multivalue expression. If null, the function returns null.
+start
+Start position. If null, the function returns null. The start argument can be negative. An index of -1 is used to specify the last value in the list.
+end
+End position. Optional; if omitted, the position at start is returned. The end argument can be negative. An index of -1 is used to specify the last value in the list.
+DescriptionReturns a subset of the multivalued field using the start and end index values.Supported types
+Example
+row a = [1, 2, 2, 3]
+| eval a1 = mv_slice(a, 1), a2 = mv_slice(a, 2, 3)
+row a = [1, 2, 2, 3]
+| eval a1 = mv_slice(a, -2), a2 = mv_slice(a, -3, -1)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sort.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sort.txt
new file mode 100644
index 0000000000000..b7b6e8ddb9cd6
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sort.txt
@@ -0,0 +1,14 @@
+MV_SORT
+
+Syntax
+Parameters
+field
+Multivalue expression. If null, the function returns null.
+order
+Sort order. The valid options are ASC and DESC, the default is ASC.
+DescriptionSorts a multivalue expression in lexicographical order.Supported types
+Example
+```esql
+ROW a = [4, 2, -3, 2]
+| EVAL sa = mv_sort(a), sd = mv_sort(a, "DESC")
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sum.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sum.txt
index f6b6fdecc6130..16f017756f4bf 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sum.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_sum.txt
@@ -1,10 +1,14 @@
MV_SUM
-Converts a multivalued field into a single valued field containing the sum
-of all of the values. For example:
+
+MV_SUM(v)
+Parameters
+v
+Multivalue expression.
+DescriptionConverts a multivalued column into a single valued column containing the sum
+of all of the values.Supported types
+Example
```esql
ROW a=[3, 5, 6]
| EVAL sum_a = MV_SUM(a)
```
-
-The input type can be any number and the output type is the same as the input type.
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_zip.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_zip.txt
new file mode 100644
index 0000000000000..788d7c6a10f3d
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-mv_zip.txt
@@ -0,0 +1,17 @@
+MV_ZIP
+
+Syntax
+Parameters
+mvLeft
+Multivalue expression.
+mvRight
+Multivalue expression.
+delim
+Delimiter. Optional; if omitted, , is used as a default delimiter.
+DescriptionCombines the values from two multivalued fields with a delimiter that joins them together.Supported types
+Example
+```esql
+ROW a = ["x", "y", "z"], b = ["1", "2"]
+| EVAL c = mv_zip(a, b, "-")
+| KEEP a, b, c
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-now.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-now.txt
index ccff8a884b0e6..e6b885f2907f2 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-now.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-now.txt
@@ -1,6 +1,14 @@
NOW
-Returns current date and time.
+Syntax
+NOW()
+DescriptionReturns current date and time.Example
```esql
ROW current_date = NOW()
```
+
+To retrieve logs from the last hour:
+```esql
+FROM sample_data
+| WHERE @timestamp > NOW() - 1 hour
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators-overview.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators-overview.txt
new file mode 100644
index 0000000000000..e72eb3b5cd9d9
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators-overview.txt
@@ -0,0 +1,14 @@
+Operators overview
+
+
+Operators
+Binary operators
+Unary operators
+Logical operators
+IS NULL and IS NOT NULL predicates
+CIDR_MATCH
+ENDS_WITH
+IN
+LIKE
+RLIKE
+STARTS_WITH
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators.txt
index 708fa25b2eae1..7fd0928973290 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-operators.txt
@@ -6,73 +6,73 @@ Equality
Equality
-Supported types:
+Supported types:Supported types
Inequality !=
Inequality !=
-Supported types:
+Supported types:Supported types
Less than <
Less than <
-Supported types:
+Supported types
Less than or equal to <=
Less than or equal to <=
-Supported types:
+Supported types
Greater than >
Greater than >
-Supported types:
+Supported types
Greater than or equal to >=
Greater than or equal to >=
-Supported types:
+Supported types
Add +
Add +
-Supported types:
+Supported types
Subtract -
Subtract -
-Supported types:
+Supported types
Multiply *
Multiply *
-Supported types:
+Supported types
Divide /
Divide /
-Supported types:
+Supported types
Modulus %
Modulus %
-Supported types:
+Supported types
Unary operators
Unary operators
The only unary operators is negation (-):
-Supported types:
+Supported types:Supported types
Logical operators
Logical operators
@@ -121,17 +121,21 @@ FROM hosts
ENDS_WITH
ENDS_WITH
-
-Returns a boolean that indicates whether a keyword string ends with another
-string:
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+suffix
+String expression. If null, the function returns null.
+DescriptionReturns a boolean that indicates whether a keyword string ends with another
+string.Supported types
+Example
```esql
FROM employees
| KEEP last_name
| EVAL ln_E = ENDS_WITH(last_name, "d")
```
-Supported types:
-
IN
IN
@@ -142,12 +146,6 @@ ROW a = 1, b = 4, c = 3
| WHERE c-a IN (3, b / 2, a)
```
-Returns a boolean that indicates whether its input is not a number.
-```esql
-ROW d = 1.0
-| EVAL s = IS_NAN(d)
-```
-
LIKE
LIKE
@@ -179,13 +177,17 @@ FROM employees
STARTS_WITH
STARTS_WITH
-
-Returns a boolean that indicates whether a keyword string starts with another
-string:
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+prefix
+String expression. If null, the function returns null.
+DescriptionReturns a boolean that indicates whether a keyword string starts with another
+string.Supported types
+Example
```esql
FROM employees
| KEEP last_name
| EVAL ln_S = STARTS_WITH(last_name, "B")
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-overview.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-overview.txt
index ea9b24ba48464..27879223d74b6 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-overview.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-overview.txt
@@ -1,6 +1,6 @@
ES|QLedit
-This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Do not use ES|QL on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
The Elasticsearch Query Language (ES|QL) provides a powerful way to filter, transform,
and analyze data stored in Elasticsearch, and in the future in other runtimes. It is
designed to be easy to learn and use, by end users, SRE teams, application
@@ -29,7 +29,7 @@ performance characteristics.
The ES|QL documentation is organized in these sections:
Getting started
A tutorial to help you get started with ES|QL.
-Learning ES|QL
+ES|QL reference
Reference documentation for the ES|QL syntax,
commands, and functions and
operators. Information about working with metadata
@@ -38,7 +38,7 @@ data processing with DISSECT and
GROK and data enrichment with ENRICH.
Using ES|QL
An overview of using the REST API, Using ES|QL in Kibana,
-Using ES|QL in Elastic Security, and Task management.
+Using ES|QL in Elastic Security, Using ES|QL across clusters, and Task management.
Limitations
The current limitations of ES|QL.
Examples
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-percentile.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-percentile.txt
index 662fa3a611a35..cc2b5e01297a0 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-percentile.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-percentile.txt
@@ -1,8 +1,15 @@
PERCENTILE
-The value at which a certain percentage of observed values occur. For example,
-the 95th percentile is the value which is greater than 95% of the observed values and
-the 50th percentile is the MEDIAN.
+Syntax
+PERCENTILE(expression, percentile)
+Parameters
+expression
+Expression from which to return a percentile.
+percentile
+A constant numeric expression.
+DescriptionReturns the value at which a certain percentage of observed values occur. For
+example, the 95th percentile is the value which is greater than 95% of the
+observed values and the 50th percentile is the MEDIAN.Example
```esql
FROM employees
| STATS p0 = PERCENTILE(salary, 0)
@@ -10,6 +17,14 @@ FROM employees
, p99 = PERCENTILE(salary, 99)
```
+The expression can use inline functions. For example, to calculate a percentile
+of the maximum values of a multivalued column, first use MV_MAX to get the
+maximum value per row, and use the result with the PERCENTILE function:
+```esql
+FROM employees
+| STATS p80_max_salary_change = PERCENTILE(MV_MAX(salary_change), 80)
+```
+
PERCENTILE is (usually) approximateeditThere are many different algorithms to calculate percentiles. The naive
implementation simply stores all the values in a sorted array. To find the 50th
percentile, you simply find the value that is at my_array[count(my_array) * 0.5].Clearly, the naive implementation does not scale — the sorted array grows
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pi.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pi.txt
index ade48755e280c..3df2811b88a88 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pi.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pi.txt
@@ -1,7 +1,8 @@
PI
-
-The ratio of a circle’s circumference to its diameter.
+Syntax
+ParametersDescriptionReturns the ratio of a circle’s circumference to its diameter.Supported types
+Example
```esql
ROW PI()
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pow.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pow.txt
index 56f1e48b73833..add982840c5ec 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pow.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-pow.txt
@@ -1,19 +1,23 @@
POW
-
-Returns the value of a base (first argument) raised to the power of an exponent (second argument).
-Both arguments must be numeric. The output is always a double. Note that it is still possible to overflow
-a double result here; in that case, null will be returned.
+Syntax
+Parameters
+base
+Numeric expression for the base. If null, the function returns null.
+exponent
+Numeric expression for the exponent. If null, the function returns null.
+DescriptionReturns the value of base raised to the power of exponent.
+It is still possible to overflow a double result here; in that case, null will be returned.
+Supported types
+Examples
```esql
ROW base = 2.0, exponent = 2
| EVAL result = POW(base, exponent)
```
-Fractional exponentseditThe exponent can be a fraction, which is similar to performing a root.
+The exponent can be a fraction, which is similar to performing a root.
For example, the exponent of 0.5 will give the square root of the base:
```esql
ROW base = 4, exponent = 0.5
| EVAL s = POW(base, exponent)
```
-
-Table of supported input and output typeseditFor clarity, the following table describes the output result type for all combinations of numeric input types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-replace.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-replace.txt
index 859f031fd6398..8a26cd2c04269 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-replace.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-replace.txt
@@ -1,7 +1,17 @@
REPLACE
-The function substitutes in the string (1st argument) any match of the regular expression (2nd argument) with the replacement string (3rd argument).If any of the arguments are NULL, the result is NULL.
-This example replaces an occurrence of the word "World" with the word "Universe":
+Syntax
+Parameters
+str
+String expression.
+regex
+Regular expression.
+newStr
+Replacement string.
+DescriptionThe function substitutes in the string str any match of the regular expression
+regex with the replacement string newStr.If any of the arguments is null, the result is null.Supported types
+ExampleThis example replaces any occurrence of the word "World" with the word
+"Universe":
```esql
ROW str = "Hello World"
| EVAL str = REPLACE(str, "World", "Universe")
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-right.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-right.txt
index b2e8fd12d3c2c..9ee98298cce43 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-right.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-right.txt
@@ -1,7 +1,14 @@
RIGHT
-
-Return the substring that extracts length chars from the string starting from the right.
+Syntax
+Parameters
+str
+The string from which to returns a substring.
+length
+The number of characters to return.
+DescriptionReturn the substring that extracts length chars from str starting
+from the right.Supported types
+Example
```esql
FROM employees
| KEEP last_name
@@ -9,5 +16,3 @@ FROM employees
| SORT last_name ASC
| LIMIT 5
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-round.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-round.txt
index 1047425f67147..6351541a7cbd0 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-round.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-round.txt
@@ -1,8 +1,13 @@
ROUND
-Rounds a number to the closest number with the specified number of digits.
-Defaults to 0 digits if no number of digits is provided. If the specified number
-of digits is negative, rounds to the number of digits left of the decimal point.
+Syntax
+Parameters
+number
+The numeric value to round. If null, the function returns null.
+decimals
+The number of decimal places to round to. Defaults to 0. If null, the function returns null.
+DescriptionRounds a number to the closest number with the specified number of digits. Defaults to 0 digits if no number of digits is provided. If the specified number of digits is negative, rounds to the number of digits left of the decimal point.Supported types
+Example
```esql
FROM employees
| KEEP first_name, last_name, height
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-rtrim.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-rtrim.txt
index d25ec734fca79..aecd6f928bf98 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-rtrim.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-rtrim.txt
@@ -1,7 +1,11 @@
RTRIM
-
-Removes trailing whitespaces from strings.
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionRemoves trailing whitespaces from strings.Supported types
+Example
```esql
ROW message = " some text ", color = " red "
| EVAL message = RTRIM(message)
@@ -9,5 +13,3 @@ ROW message = " some text ", color = " red "
| EVAL message = CONCAT("'", message, "'")
| EVAL color = CONCAT("'", color, "'")
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-show.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-show.txt
index dc6b80b9eba49..4941c1364a95b 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-show.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-show.txt
@@ -7,7 +7,7 @@ SHOW item
Parameters
item
-Can be INFO or FUNCTIONS.
+Can only be INFO.
DescriptionThe
```esql
SHOW source command returns information about the deployment and
@@ -19,14 +19,7 @@ Use
SHOW INFO to return the deployment’s version, build date and hash.
```
-Use
-```esql
-SHOW FUNCTIONS to return a list of all supported functions and a
-```
-
-synopsis of each function.
Examples
```esql
-SHOW functions
-| WHERE STARTS_WITH(name, "is_")
+SHOW INFO
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-signum.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-signum.txt
new file mode 100644
index 0000000000000..5cc3dd25c95c8
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-signum.txt
@@ -0,0 +1,12 @@
+SIGNUM
+
+Syntax
+Parameters
+number
+Numeric expression. If null, the function returns null.
+DescriptionReturns the sign of the given number. It returns -1 for negative numbers, 0 for 0 and 1 for positive numbers.Supported types
+Example
+```esql
+ROW d = 100.0
+| EVAL s = SIGNUM(d)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sin.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sin.txt
index b0e05c95cb186..97f21c4066cde 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sin.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sin.txt
@@ -1,10 +1,12 @@
SIN
-
-Sine trigonometric function. Input expected in radians.
+Syntax
+Parameters
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns ths Sine trigonometric function of an angle.Supported types
+Example
```esql
ROW a=1.8
| EVAL sin=SIN(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sinh.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sinh.txt
index dfa5f203d8f16..71f1e7224ec98 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sinh.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sinh.txt
@@ -1,10 +1,12 @@
SINH
-
-Sine hyperbolic function.
+Syntax
+Parameters
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns the hyperbolic sine of an angle.Supported types
+Example
```esql
ROW a=1.8
| EVAL sinh=SINH(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-split.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-split.txt
index 834f8a10507ef..c9161c7863139 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-split.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-split.txt
@@ -1,10 +1,14 @@
SPLIT
-Split a single valued string into multiple strings. For example:
+
+Parameters
+str
+String expression. If null, the function returns null.
+delim
+Delimiter. Only single byte delimiters are currently supported.
+DescriptionSplits a single valued string into multiple strings.Supported types
+Example
```esql
ROW words="foo;bar;baz;qux;quux;corge"
| EVAL word = SPLIT(words, ";")
```
-
-Which splits "foo;bar;baz;qux;quux;corge" on ; and returns an array:
-Only single byte delimiters are currently supported.
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sqrt.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sqrt.txt
index 0fef30ccbdf5d..68ee934e079ca 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sqrt.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sqrt.txt
@@ -1,11 +1,14 @@
SQRT
-
-Returns the square root of a number. The input can be any numeric value, the return value
-is always a double.Square roots of negative numbers are NaN. Square roots of infinites are infinite.
+Syntax
+Parameters
+n
+Numeric expression. If null, the function returns null.
+DescriptionReturns the square root of a number. The input can be any numeric value, the
+return value is always a double.Square roots of negative numbers are NaN. Square roots of infinites are
+infinite.Supported types
+Example
```esql
ROW d = 100.0
| EVAL s = SQRT(d)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_centroid_agg.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_centroid_agg.txt
new file mode 100644
index 0000000000000..3cba219549177
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_centroid_agg.txt
@@ -0,0 +1,11 @@
+ST_CENTROID_AGG
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Calculate the spatial centroid over a field with spatial point geometry type.
+```esql
+FROM airports
+| STATS centroid=ST_CENTROID_AGG(location)
+```
+
+Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_contains.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_contains.txt
new file mode 100644
index 0000000000000..5c271d6d40de0
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_contains.txt
@@ -0,0 +1,19 @@
+ST_CONTAINS
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+geomA
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+geomB
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+The second parameter must also have the same coordinate system as the first.
+This means it is not possible to combine geo_* and cartesian_* parameters.
+DescriptionReturns whether the first geometry contains the second geometry.This is the inverse of the ST_WITHIN function.Supported types
+Example
+```esql
+FROM airport_city_boundaries
+| WHERE ST_CONTAINS(city_boundary, TO_GEOSHAPE("POLYGON((109.35 18.3, 109.45 18.3, 109.45 18.4, 109.35 18.4, 109.35 18.3))"))
+| KEEP abbrev, airport, region, city, city_location
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_disjoint.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_disjoint.txt
new file mode 100644
index 0000000000000..401f94c2bf861
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_disjoint.txt
@@ -0,0 +1,20 @@
+ST_DISJOINT
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+geomA
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+geomB
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+The second parameter must also have the same coordinate system as the first.
+This means it is not possible to combine geo_* and cartesian_* parameters.
+DescriptionReturns whether the two geometries or geometry columns are disjoint.This is the inverse of the ST_INTERSECTS function.
+In mathematical terms: ST_Disjoint(A, B) ⇔ A ⋂ B = ∅Supported types
+Example
+```esql
+FROM airport_city_boundaries
+| WHERE ST_DISJOINT(city_boundary, TO_GEOSHAPE("POLYGON((-10 -60, 120 -60, 120 60, -10 60, -10 -60))"))
+| KEEP abbrev, airport, region, city, city_location
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_intersects.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_intersects.txt
new file mode 100644
index 0000000000000..1dbb4a50122d6
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_intersects.txt
@@ -0,0 +1,22 @@
+ST_INTERSECTS
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+geomA
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+geomB
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+The second parameter must also have the same coordinate system as the first.
+This means it is not possible to combine geo_* and cartesian_* parameters.
+DescriptionReturns true if two geometries intersect.
+They intersect if they have any point in common, including their interior points
+(points along lines or within polygons).
+This is the inverse of the ST_DISJOINT function.
+In mathematical terms: ST_Intersects(A, B) ⇔ A ⋂ B ≠ ∅Supported types
+Example
+```esql
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((42 14, 43 14, 43 15, 42 15, 42 14))"))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_within.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_within.txt
new file mode 100644
index 0000000000000..0224b921d274c
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_within.txt
@@ -0,0 +1,19 @@
+ST_WITHIN
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+geomA
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+geomB
+Expression of type geo_point, cartesian_point, geo_shape or cartesian_shape. If null, the function returns null.
+The second parameter must also have the same coordinate system as the first.
+This means it is not possible to combine geo_* and cartesian_* parameters.
+DescriptionReturns whether the first geometry is within the second geometry.This is the inverse of the ST_CONTAINS function.Supported types
+Example
+```esql
+FROM airport_city_boundaries
+| WHERE ST_WITHIN(city_boundary, TO_GEOSHAPE("POLYGON((109.1 18.15, 109.6 18.15, 109.6 18.65, 109.1 18.65, 109.1 18.15))"))
+| KEEP abbrev, airport, region, city, city_location
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_x.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_x.txt
new file mode 100644
index 0000000000000..286c9471b71d8
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_x.txt
@@ -0,0 +1,15 @@
+ST_X
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+point
+Expression of type geo_point or cartesian_point. If null, the function returns null.
+DescriptionExtracts the x coordinate from the supplied point.
+If the points is of type geo_point this is equivalent to extracting the longitude value.Supported types
+Example
+```esql
+ROW point = TO_GEOPOINT("POINT(42.97109629958868 14.7552534006536)")
+| EVAL x = ST_X(point), y = ST_Y(point)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_y.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_y.txt
new file mode 100644
index 0000000000000..dd0262318d862
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-st_y.txt
@@ -0,0 +1,15 @@
+ST_Y
+
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+Parameters
+point
+Expression of type geo_point or cartesian_point. If null, the function returns null.
+DescriptionExtracts the y coordinate from the supplied point.
+If the points is of type geo_point this is equivalent to extracting the latitude value.Supported types
+Example
+```esql
+ROW point = TO_GEOPOINT("POINT(42.97109629958868 14.7552534006536)")
+| EVAL x = ST_X(point), y = ST_Y(point)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-stats.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-stats.txt
index 971befa8d5626..ed6dbbd283b6b 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-stats.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-stats.txt
@@ -1,15 +1,17 @@
STATS ... BY
Syntax
-STATS [column1 =] expression1[, ..., [columnN =] expressionN] [BY grouping_column1[, ..., grouping_columnN]]
+STATS [column1 =] expression1[, ..., [columnN =] expressionN]
+[BY grouping_expression1[, ..., grouping_expressionN]]
Parameters
columnX
The name by which the aggregated value is returned. If omitted, the name is
equal to the corresponding expression (expressionX).
expressionX
An expression that computes an aggregated value.
-grouping_columnX
-The column containing the values to group by.
+grouping_expressionX
+An expression that outputs the values to group by.
+Individual null values are skipped when computing aggregations.
DescriptionThe STATS ... BY processing command groups rows according to a common value
and calculate one or more aggregated values over the grouped rows. If BY is
omitted, the output table contains exactly one row with the aggregations applied
@@ -22,10 +24,14 @@ MEDIAN
MEDIAN_ABSOLUTE_DEVIATION
MIN
PERCENTILE
+[preview]
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+ST_CENTROID_AGG
SUM
+VALUES
STATS without any groups is much much faster than adding a group.
-Grouping on a single column is currently much more optimized than grouping
- on many columns. In some tests we have seen grouping on a single keyword
+Grouping on a single expression is currently much more optimized than grouping
+ on many expressions. In some tests we have seen grouping on a single keyword
column to be five times faster than grouping on two keyword columns. Do
not try to work around this by combining the two columns together with
something like CONCAT and then grouping - that is not going to be
@@ -59,3 +65,37 @@ FROM employees
| EVAL avg_salary = ROUND(avg_salary)
| SORT hired, languages.long
```
+
+Both the aggregating functions and the grouping expressions accept other
+functions. This is useful for using STATS...BY on multivalue columns.
+For example, to calculate the average salary change, you can use MV_AVG to
+first average the multiple values per employee, and use the result with the
+AVG function:
+```esql
+FROM employees
+| STATS avg_salary_change = ROUND(AVG(MV_AVG(salary_change)), 10)
+```
+
+An example of grouping by an expression is grouping employees on the first
+letter of their last name:
+```esql
+FROM employees
+| STATS my_count = COUNT() BY LEFT(last_name, 1)
+| SORT `LEFT(last_name, 1)`
+```
+
+Specifying the output column name is optional. If not specified, the new column
+name is equal to the expression. The following query returns a column named
+AVG(salary):
+```esql
+FROM employees
+| STATS AVG(salary)
+```
+
+Because this name contains special characters, it needs to be
+quoted with backticks (`) when using it in subsequent commands:
+```esql
+FROM employees
+| STATS AVG(salary)
+| EVAL avg_salary_rounded = ROUND(`AVG(salary)`)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-substring.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-substring.txt
index e4a2460485810..1b68352b243eb 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-substring.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-substring.txt
@@ -1,7 +1,17 @@
SUBSTRING
-Returns a substring of a string, specified by a start position and an optional
-length. This example returns the first three characters of every last name:
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+start
+Start position.
+length
+Length of the substring from the start position. Optional; if omitted, all
+positions after start are returned.
+DescriptionReturns a substring of a string, specified by a start position and an optional
+length.Supported types
+ExamplesThis example returns the first three characters of every last name:
```esql
FROM employees
| KEEP last_name
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sum.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sum.txt
index 36686979ebed6..c2cdff2787d54 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sum.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-sum.txt
@@ -1,7 +1,19 @@
SUM
-The sum of a numeric field.
+Syntax
+SUM(expression)
+expression
+Numeric expression.
+DescriptionReturns the sum of a numeric expression.Example
```esql
FROM employees
| STATS SUM(languages)
```
+
+The expression can use inline functions. For example, to calculate
+the sum of each employee’s maximum salary changes, apply the
+MV_MAX function to each row and then sum the results:
+```esql
+FROM employees
+| STATS total_salary_changes = SUM(MV_MAX(salary_change))
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-syntax.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-syntax.txt
index 7ad197c9480f5..d14570ae7fd35 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-syntax.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-syntax.txt
@@ -14,30 +14,22 @@ line. However, you can write an ES|QL query as a single line. The following
query is identical to the previous one:
source-command | processing-command1 | processing-command2
Identifiersedit
-The identifiers can be used as they are and don’t require quoting, unless
-containing special characters, in which case they must be quoted with
-backticks (```). What "special characters" means is command dependent.
-For FROM, KEEP, DROP,
-RENAME, MV_EXPAND and
-ENRICH these are: `=`, ```, `,`, ` ` (space), `|` ,
-`[`, `]`, `\t` (TAB), `\r` (CR), `\n` (LF); one `/` is allowed unquoted, but
-a sequence of two or more require quoting.
-The rest of the commands - those allowing for identifiers be used in
-expressions - require quoting if the identifier contains characters other than
-letters, numbers and `_` and doesn’t start with a letter, `_` or `@`.
-For instance:
-// Retain just one field
+Identifiers need to be quoted with backticks (```) if:
+they don’t start with a letter, `_` or `@`
+any of the other characters is not a letter, number, or `_`
+For example:
```esql
FROM index
-| KEEP 1.field
+| KEEP `1.field`
```
-is legal. However, if same field is to be used with an EVAL,
-it’d have to be quoted:
-// Copy one field
+When referencing a function alias that itself uses a quoted identifier, the
+backticks of the quoted identifier need to be escaped with another backtick. For
+example:
```esql
FROM index
-| EVAL my_field = `1.field`
+| STATS COUNT(`1.field`)
+| EVAL my_count = `COUNT(``1.field``)`
```
Literalsedit
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tan.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tan.txt
index 5706ad3a77f91..4d939401e3035 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tan.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tan.txt
@@ -1,10 +1,12 @@
TAN
-
-Tangent trigonometric function. Input expected in radians.
+Syntax
+Parameters
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns the Tangent trigonometric function of an angle.Supported types
+Example
```esql
ROW a=1.8
| EVAL tan=TAN(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tanh.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tanh.txt
index 02391cb32bf46..ffafd176e8c49 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tanh.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tanh.txt
@@ -1,10 +1,12 @@
TANH
-
-Tangent hyperbolic function.
+Syntax
+Parameters
+angle
+An angle, in radians. If null, the function returns null.
+DescriptionReturns the Tangent hyperbolic function of an angle.Supported types
+Example
```esql
ROW a=1.8
| EVAL tanh=TANH(a)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tau.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tau.txt
index 600c43f47ccf9..cab47b8262642 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tau.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-tau.txt
@@ -1,7 +1,8 @@
TAU
-
-The ratio of a circle’s circumference to its radius.
+Syntax
+DescriptionReturns the ratio of a circle’s circumference
+to its radius.Example
```esql
ROW TAU()
```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_boolean.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_boolean.txt
index 498f40679b129..0d40e789643ca 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_boolean.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_boolean.txt
@@ -1,13 +1,15 @@
TO_BOOLEAN
-Converts an input value to a boolean value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a string or numeric type.A string value of "true" will be case-insensitive converted to the Boolean
+AliasTO_BOOLSyntax
+TO_BOOLEAN(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to a boolean value.A string value of "true" will be case-insensitive converted to the Boolean
true. For anything else, including the empty string, the function will
-return false. For example:
+return false.The numerical value of 0 will be converted to false, anything else will be
+converted to true.Supported typesThe input type must be of a string or numeric type.Example
```esql
ROW str = ["true", "TRuE", "false", "", "yes", "1"]
| EVAL bool = TO_BOOLEAN(str)
```
-
-The numerical value of 0 will be converted to false, anything else will be
-converted to true.Alias: TO_BOOL
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianpoint.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianpoint.txt
index e50645f73fb45..35b32c890c4d0 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianpoint.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianpoint.txt
@@ -1,8 +1,15 @@
TO_CARTESIANPOINT
-Converts an input value to a point value.The input can be a single- or multi-valued field or an expression.
-The input type must be a string or a cartesian point.A string will only be successfully converted if it respects the
-WKT Point format:
-row wkt = ["POINT(4297.11 -1475.53)", "POINT(7580.93 2272.77)"]
-| mv_expand wkt
-| eval pt = to_cartesianpoint(wkt)
+Syntax
+TO_CARTESIANPOINT(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to a point value.A string will only be successfully converted if it respects the
+WKT Point format.Supported types
+Example
+```esql
+ROW wkt = ["POINT(4297.11 -1475.53)", "POINT(7580.93 2272.77)"]
+| MV_EXPAND wkt
+| EVAL pt = TO_CARTESIANPOINT(wkt)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianshape.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianshape.txt
new file mode 100644
index 0000000000000..12deb50d56a53
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_cartesianshape.txt
@@ -0,0 +1,16 @@
+TO_CARTESIANSHAPE
+
+Syntax
+TO_CARTESIANSHAPE(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+The input type must be a string, a cartesian_shape or a cartesian_point.
+DescriptionConverts an input value to a cartesian_shape value.A string will only be successfully converted if it respects the
+WKT format.Supported types
+Example
+```esql
+ROW wkt = ["POINT(4297.11 -1475.53)", "POLYGON ((3339584.72 1118889.97, 4452779.63 4865942.27, 2226389.81 4865942.27, 1113194.90 2273030.92, 3339584.72 1118889.97))"]
+| MV_EXPAND wkt
+| EVAL geom = TO_CARTESIANSHAPE(wkt)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_datetime.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_datetime.txt
index 534175b0824fd..ea715bf1e5794 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_datetime.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_datetime.txt
@@ -1,8 +1,13 @@
TO_DATETIME
-Converts an input value to a date value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a string or numeric type.A string will only be successfully converted if it’s respecting the format
-yyyy-MM-dd'T'HH:mm:ss.SSS'Z' (to convert dates in other formats, use DATE_PARSE). For example:
+AliasTO_DTSyntax
+TO_DATETIME(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to a date value.A string will only be successfully converted if it’s respecting the format
+yyyy-MM-dd'T'HH:mm:ss.SSS'Z'. To convert dates in other formats, use
+DATE_PARSE.Supported typesThe input type must be of a string or numeric type.Examples
```esql
ROW string = ["1953-09-02T00:00:00.000Z", "1964-06-02T00:00:00.000Z", "1964-06-02 00:00:00"]
| EVAL datetime = TO_DATETIME(string)
@@ -13,11 +18,8 @@ field has not been converted. The reason being that if the date format is not
respected, the conversion will result in a null value. When this happens a
Warning header is added to the response. The header will provide information
on the source of the failure:"Line 1:112: evaluation of [TO_DATETIME(string)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.IllegalArgumentException: failed to parse date field [1964-06-02 00:00:00] with format [yyyy-MM-dd'T'HH:mm:ss.SSS'Z']"If the input parameter is of a numeric type, its value will be interpreted as
-milliseconds since the Unix epoch.
-For example:
+milliseconds since the Unix epoch. For example:
```esql
ROW int = [0, 1]
| EVAL dt = TO_DATETIME(int)
```
-
-Alias: TO_DT
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_degrees.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_degrees.txt
index ed433509bae03..a0facc2230b87 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_degrees.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_degrees.txt
@@ -1,8 +1,12 @@
TO_DEGREES
-Converts a number in radians
-to degrees.The input can be a single- or multi-valued field or an expression. The input
-type must be of a numeric type and result is always double.Example:
+Syntax
+TO_DEGREES(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts a number in radians to
+degrees.Supported typesThe input type must be of a numeric type and result is always double.Example
```esql
ROW rad = [1.57, 3.14, 4.71]
| EVAL deg = TO_DEGREES(rad)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_double.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_double.txt
index c5890745988b1..67604c80eb48c 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_double.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_double.txt
@@ -1,15 +1,18 @@
TO_DOUBLE
-Converts an input value to a double value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a boolean, date, string or numeric type.Example:
+AliasTO_DBLSyntax
+TO_DOUBLE(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to a double value.If the input parameter is of a date type, its value will be interpreted as
+milliseconds since the Unix epoch, converted to double.Boolean true will be converted to double 1.0, false to 0.0.Supported typesThe input type must be of a boolean, date, string or numeric type.Example
```esql
ROW str1 = "5.20128E11", str2 = "foo"
| EVAL dbl = TO_DOUBLE("520128000000"), dbl1 = TO_DOUBLE(str1), dbl2 = TO_DOUBLE(str2)
```
-Note that in this example, the last conversion of the string isn’t
-possible. When this happens, the result is a null value. In this case a
-Warning header is added to the response. The header will provide information
-on the source of the failure:"Line 1:115: evaluation of [TO_DOUBLE(str2)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: For input string: \"foo\""If the input parameter is of a date type, its value will be interpreted as
-milliseconds since the Unix epoch,
-converted to double.Boolean true will be converted to double 1.0, false to 0.0.Alias: TO_DBL
\ No newline at end of file
+Note that in this example, the last conversion of the string isn’t possible.
+When this happens, the result is a null value. In this case a Warning header
+is added to the response. The header will provide information on the source of
+the failure:"Line 1:115: evaluation of [TO_DOUBLE(str2)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: For input string: \"foo\""
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geopoint.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geopoint.txt
index 4aabac29f8c6d..3fb5889485e17 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geopoint.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geopoint.txt
@@ -1,7 +1,15 @@
TO_GEOPOINT
-Converts an input value to a geo_point value.The input can be a single- or multi-valued field or an expression.
-The input type must be a string or a geo_point.A string will only be successfully converted if it respects the
-WKT Point format:
-row wkt = "POINT(42.97109630194 14.7552534413725)"
-| eval pt = to_geopoint(wkt)
+Syntax
+TO_GEOPOINT(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+The input type must be a string or a geo_point.
+DescriptionConverts an input value to a geo_point value.Supported types
+A string will only be successfully converted if it respects the
+WKT Point format.Example
+```esql
+ROW wkt = "POINT(42.97109630194 14.7552534413725)"
+| EVAL pt = TO_GEOPOINT(wkt)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geoshape.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geoshape.txt
new file mode 100644
index 0000000000000..99c3995a6f393
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_geoshape.txt
@@ -0,0 +1,15 @@
+TO_GEOSHAPE
+
+Syntax
+TO_GEOPOINT(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+The input type must be a string, a geo_shape or a geo_point.
+DescriptionConverts an input value to a geo_shape value.A string will only be successfully converted if it respects the
+WKT format.Supported types
+Example
+```esql
+ROW wkt = "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))"
+| EVAL geom = TO_GEOSHAPE(wkt)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_integer.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_integer.txt
index e1a43e83e3995..e11b210f7bd07 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_integer.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_integer.txt
@@ -1,7 +1,12 @@
TO_INTEGER
-Converts an input value to an integer value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a boolean, date, string or numeric type.Example:
+AliasTO_INTSyntax
+TO_INTEGER(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to an integer value.If the input parameter is of a date type, its value will be interpreted as
+milliseconds since the Unix epoch, converted to integer.Boolean true will be converted to integer 1, false to 0.Supported typesThe input type must be of a boolean, date, string or numeric type.Example
```esql
ROW long = [5013792, 2147483647, 501379200000]
| EVAL int = TO_INTEGER(long)
@@ -10,6 +15,4 @@ ROW long = [5013792, 2147483647, 501379200000]
Note that in this example, the last value of the multi-valued field cannot
be converted as an integer. When this happens, the result is a null value.
In this case a Warning header is added to the response. The header will
-provide information on the source of the failure:"Line 1:61: evaluation of [TO_INTEGER(long)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"org.elasticsearch.xpack.ql.QlIllegalArgumentException: [501379200000] out of [integer] range"If the input parameter is of a date type, its value will be interpreted as
-milliseconds since the Unix epoch,
-converted to integer.Boolean true will be converted to integer 1, false to 0.Alias: TO_INT
\ No newline at end of file
+provide information on the source of the failure:"Line 1:61: evaluation of [TO_INTEGER(long)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"org.elasticsearch.xpack.ql.InvalidArgumentException: [501379200000] out of [integer] range"
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_ip.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_ip.txt
index 23c8422170cfc..d19f49388bf86 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_ip.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_ip.txt
@@ -1,13 +1,18 @@
TO_IP
-Converts an input string to an IP value.The input can be a single- or multi-valued field or an expression.Example:
+Syntax
+TO_IP(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input string to an IP value.Example
```esql
ROW str1 = "1.1.1.1", str2 = "foo"
| EVAL ip1 = TO_IP(str1), ip2 = TO_IP(str2)
| WHERE CIDR_MATCH(ip1, "1.0.0.0/8")
```
-Note that in the example above the last conversion of the string isn’t
-possible. When this happens, the result is a null value. In this case a
-Warning header is added to the response. The header will provide information
-on the source of the failure:"Line 1:68: evaluation of [TO_IP(str2)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.IllegalArgumentException: 'foo' is not an IP string literal."
\ No newline at end of file
+Note that in this example, the last conversion of the string isn’t possible.
+When this happens, the result is a null value. In this case a Warning header
+is added to the response. The header will provide information on the source of
+the failure:"Line 1:68: evaluation of [TO_IP(str2)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.IllegalArgumentException: 'foo' is not an IP string literal."
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_long.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_long.txt
index 802c09d7cf35d..04ec7b6e79a0e 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_long.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_long.txt
@@ -1,7 +1,12 @@
TO_LONG
-Converts an input value to a long value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a boolean, date, string or numeric type.Example:
+Syntax
+TO_LONG(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to a long value.If the input parameter is of a date type, its value will be interpreted as
+milliseconds since the Unix epoch, converted to long.Boolean true will be converted to long 1, false to 0.Supported typesThe input type must be of a boolean, date, string or numeric type.Example
```esql
ROW str1 = "2147483648", str2 = "2147483648.2", str3 = "foo"
| EVAL long1 = TO_LONG(str1), long2 = TO_LONG(str2), long3 = TO_LONG(str3)
@@ -10,6 +15,4 @@ ROW str1 = "2147483648", str2 = "2147483648.2", str3 = "foo"
Note that in this example, the last conversion of the string isn’t
possible. When this happens, the result is a null value. In this case a
Warning header is added to the response. The header will provide information
-on the source of the failure:"Line 1:113: evaluation of [TO_LONG(str3)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: For input string: \"foo\""If the input parameter is of a date type, its value will be interpreted as
-milliseconds since the Unix epoch,
-converted to long.Boolean true will be converted to long 1, false to 0.
\ No newline at end of file
+on the source of the failure:"Line 1:113: evaluation of [TO_LONG(str3)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: For input string: \"foo\""
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_lower.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_lower.txt
new file mode 100644
index 0000000000000..34c99788884b3
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_lower.txt
@@ -0,0 +1,12 @@
+TO_LOWER
+
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionReturns a new string representing the input string converted to lower case.Supported types
+Example
+```esql
+ROW message = "Some Text"
+| EVAL message_lower = TO_LOWER(message)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_radians.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_radians.txt
index 9b40d70c52bc8..966d38424db6c 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_radians.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_radians.txt
@@ -1,8 +1,12 @@
TO_RADIANS
-Converts a number in degrees to
-radians.The input can be a single- or multi-valued field or an expression. The input
-type must be of a numeric type and result is always double.Example:
+Syntax
+TO_RADIANS(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts a number in degrees to
+radians.Supported typesThe input type must be of a numeric type and result is always double.Example
```esql
ROW deg = [90.0, 180.0, 270.0]
| EVAL rad = TO_RADIANS(deg)
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_string.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_string.txt
index ec08289046414..72d19e8808a72 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_string.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_string.txt
@@ -1,7 +1,11 @@
TO_STRING
-
-Converts a field into a string. For example:
+AliasTO_STR
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value into a string.Supported types
+Example
```esql
ROW a=10
| EVAL j = TO_STRING(a)
@@ -12,5 +16,3 @@ It also works fine on multivalued fields:
ROW a=[10, 9, 8]
| EVAL j = TO_STRING(a)
```
-
-Alias: TO_STRSupported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_unsigned_long.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_unsigned_long.txt
index 22c8a680c8c5b..ea7fd27267950 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_unsigned_long.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_unsigned_long.txt
@@ -1,7 +1,15 @@
TO_UNSIGNED_LONG
-Converts an input value to an unsigned long value.The input can be a single- or multi-valued field or an expression. The input
-type must be of a boolean, date, string or numeric type.Example:
+
+This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+AliasesTO_ULONG, TO_ULSyntax
+TO_UNSIGNED_LONG(v)
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input value to an unsigned long value.Supported typesThe input type must be of a boolean, date, string or numeric type.If the input parameter is of a date type, its value will be interpreted as
+milliseconds since the Unix epoch, converted to unsigned
+long.Boolean true will be converted to unsigned long 1, false to 0.Example
```esql
ROW str1 = "2147483648", str2 = "2147483648.2", str3 = "foo"
| EVAL long1 = TO_UNSIGNED_LONG(str1), long2 = TO_ULONG(str2), long3 = TO_UL(str3)
@@ -10,6 +18,4 @@ ROW str1 = "2147483648", str2 = "2147483648.2", str3 = "foo"
Note that in this example, the last conversion of the string isn’t
possible. When this happens, the result is a null value. In this case a
Warning header is added to the response. The header will provide information
-on the source of the failure:"Line 1:133: evaluation of [TO_UL(str3)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: Character f is neither a decimal digit number, decimal point, nor \"e\" notation exponential mark."If the input parameter is of a date type, its value will be interpreted as
-milliseconds since the Unix epoch,
-converted to unsigned long.Boolean true will be converted to unsigned long 1, false to 0.Alias: TO_ULONG, TO_UL
\ No newline at end of file
+on the source of the failure:"Line 1:133: evaluation of [TO_UL(str3)] failed, treating result as null. Only first 20 failures recorded."A following header will contain the failure reason and the offending value:"java.lang.NumberFormatException: Character f is neither a decimal digit number, decimal point, nor \"e\" notation exponential mark."
\ No newline at end of file
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_upper.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_upper.txt
new file mode 100644
index 0000000000000..c1ad388758dae
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_upper.txt
@@ -0,0 +1,12 @@
+TO_UPPER
+
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionReturns a new string representing the input string converted to upper case.Supported types
+Example
+```esql
+ROW message = "Some Text"
+| EVAL message_upper = TO_UPPER(message)
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_version.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_version.txt
index 9e95474fecd05..8447c717bc1d5 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_version.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-to_version.txt
@@ -1,9 +1,11 @@
TO_VERSION
-
-Converts an input string to a version value. For example:
+AliasTO_VERSyntax
+Parameters
+v
+Input value. The input can be a single- or multi-valued column or an expression.
+DescriptionConverts an input string to a version value.Supported types
+Example
```esql
ROW v = TO_VERSION("1.2.3")
```
-
-The input can be a single- or multi-valued field or an expression.Alias: TO_VERSupported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-trim.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-trim.txt
index 91910162681d9..a73cd1ec7ce39 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-trim.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-trim.txt
@@ -1,11 +1,13 @@
TRIM
-
-Removes leading and trailing whitespaces from strings.
+Syntax
+Parameters
+str
+String expression. If null, the function returns null.
+DescriptionRemoves leading and trailing whitespaces from strings.Supported types
+Example
```esql
ROW message = " some text ", color = " red "
| EVAL message = TRIM(message)
| EVAL color = TRIM(color)
```
-
-Supported types:
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-values.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-values.txt
new file mode 100644
index 0000000000000..0b3a1ec5db2a9
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-values.txt
@@ -0,0 +1,22 @@
+VALUES
+
+
+Do not use VALUES on production environments. This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
+Syntax
+VALUES(expression)
+expression
+Expression of any type except geo_point, cartesian_point, geo_shape, or cartesian_shape.
+DescriptionReturns all values in a group as a multivalued field. The order of the returned values isn’t guaranteed.
+If you need the values returned in order use MV_SORT.
+This can use a significant amount of memory and ES|QL doesn’t yet
+ grow aggregations beyond memory. So this aggregation will work until
+ it is used to collect more values than can fit into memory. Once it
+ collects too many values it will fail the query with
+ a Circuit Breaker Error.
+Example
+```esql
+FROM employees
+| EVAL first_letter = SUBSTRING(first_name, 0, 1)
+| STATS first_name=MV_SORT(VALUES(first_name)) BY first_letter
+| SORT first_letter
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-where.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-where.txt
index 49343c329fbcc..808d38d34ab5d 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-where.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/esql_docs/esql-where.txt
@@ -20,6 +20,13 @@ FROM employees
| WHERE still_hired
```
+Use date math to retrieve data from a specific time range. For example, to
+retrieve the last hour of logs:
+```esql
+FROM sample_data
+| WHERE @timestamp > NOW() - 1 hour
+```
+
WHERE supports various functions. For example the
LENGTH function:
```esql
@@ -28,7 +35,7 @@ FROM employees
| WHERE LENGTH(first_name) < 4
```
-For a complete list of all functions, refer to Functions and operators.For NULL comparison, use the IS NULL and IS NOT NULL predicates:
+For a complete list of all functions, refer to Functions overview.For NULL comparison, use the IS NULL and IS NOT NULL predicates:
```esql
FROM employees
| WHERE birth_date IS NULL
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts
index 621fd60306497..3ef90e53842b2 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/index.ts
@@ -77,7 +77,7 @@ export function registerQueryFunction({
{
name: 'execute_query',
contexts: ['core'],
- visibility: FunctionVisibility.AssistantOnly,
+ visibility: FunctionVisibility.UserOnly,
description: 'Display the results of an ES|QL query',
parameters: {
type: 'object',
@@ -108,17 +108,8 @@ export function registerQueryFunction({
{
name: 'query',
contexts: ['core'],
- description: `This function generates, executes and/or visualizes a query based on the user's request. It also explains how ES|QL works and how to convert queries from one language to another.`,
+ description: `This function generates, executes and/or visualizes a query based on the user's request. It also explains how ES|QL works and how to convert queries from one language to another. This function takes no arguments.`,
visibility: FunctionVisibility.AssistantOnly,
- parameters: {
- type: 'object',
- properties: {
- switch: {
- type: 'boolean',
- },
- },
- required: ['switch'],
- } as const,
},
async ({ messages, connectorId, chat }, signal) => {
const [systemMessage, esqlDocs] = await Promise.all([loadSystemMessage(), loadEsqlDocs()]);
@@ -174,13 +165,14 @@ export function registerQueryFunction({
${VisualizeESQLUserIntention.visualizeXy}
Some examples:
- "Show me the avg of x" => ${VisualizeESQLUserIntention.executeAndReturnResults}
- "Show me the results of y" => ${VisualizeESQLUserIntention.executeAndReturnResults}
- "Display the sum of z" => ${VisualizeESQLUserIntention.executeAndReturnResults}
"I want a query that ..." => ${VisualizeESQLUserIntention.generateQueryOnly}
"... Just show me the query" => ${VisualizeESQLUserIntention.generateQueryOnly}
"Create a query that ..." => ${VisualizeESQLUserIntention.generateQueryOnly}
+
+ "Show me the avg of x" => ${VisualizeESQLUserIntention.executeAndReturnResults}
+ "Show me the results of y" => ${VisualizeESQLUserIntention.executeAndReturnResults}
+ "Display the sum of z" => ${VisualizeESQLUserIntention.executeAndReturnResults}
"Show me the avg of x over time" => ${VisualizeESQLUserIntention.visualizeAuto}
"I want a bar chart of ... " => ${VisualizeESQLUserIntention.visualizeBar}
@@ -297,6 +289,8 @@ export function registerQueryFunction({
\`\`\`esql
\`\`\`
+
+ Respond in plain text. Do not attempt to use a function.
Prefer to use commands and functions for which you have requested documentation.
@@ -332,18 +326,6 @@ export function registerQueryFunction({
to ES|QL, make sure that the functions are available and documented in ES|QL.
E.g., for SPL's LEN, use LENGTH. For IF, use CASE.
- Directive: ONLY use aggregation functions in STATS commands, and use ONLY aggregation
- functions in stats commands, NOT in SORT or EVAL.
- Rationale: Only aggregation functions are supported in STATS commands, and aggregation
- functions are only supported in STATS commands.
- Action: Create new columns using EVAL first and then aggregate over them in STATS commands.
- Do not use aggregation functions anywhere else, such as SORT or EVAL.
- Example:
- \`\`\`esql
- EVAL is_failure_as_number = CASE(event.outcome == "failure", 1, 0)
- | STATS total_failures = SUM(is_failure_as_number) BY my_grouping_name
- \`\`\`
-
`,
},
},
@@ -398,7 +380,7 @@ export function registerQueryFunction({
},
};
}),
- startWith(createFunctionResponseMessage({ name: 'query', content: { switch: true } }))
+ startWith(createFunctionResponseMessage({ name: 'query', content: {} }))
);
}
);
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/system_message.txt b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/system_message.txt
index 7b749062759a5..b53d6cba32076 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/system_message.txt
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_app/server/functions/query/system_message.txt
@@ -73,101 +73,112 @@ is 10000.
## Functions and operators
### Aggregation functions
-- AVG
-- COUNT
-- COUNT_DISTINCT
-- MAX
-- MEDIAN
-- MEDIAN_ABSOLUTE_DEVIATION
-- MIN
-- PERCENTILE
-- SUM
+AVG
+COUNT
+COUNT_DISTINCT
+MAX
+MEDIAN
+MEDIAN_ABSOLUTE_DEVIATION
+MIN
+PERCENTILE
+ST_CENTROID
+SUM
+VALUES
### Mathematical functions
-- ABS
-- ACOS
-- ASIN
-- ATAN
-- ATAN2
-- CEIL
-- COS
-- COSH
-- E
-- FLOOR
-- LOG10
-- PI
-- POW
-- ROUND
-- SIN
-- SINH
-- SQRT
-- TAN
-- TANH
-- TAU
+ABS
+ACOS
+ASIN
+ATAN
+ATAN2
+CEIL
+COS
+COSH
+E
+FLOOR
+LOG
+LOG10
+PI
+POW
+ROUND
+SIN
+SINH
+SQRT
+TAN
+TANH
+TAU
### String functions
-- CONCAT
-- LEFT
-- LENGTH
-- LTRIM
-- REPLACE
-- RIGHT
-- RTRIM
-- SPLIT
-- SUBSTRING
-- TRIM
+CONCAT
+LEFT
+LENGTH
+LTRIM
+REPLACE
+RIGHT
+RTRIM
+SPLIT
+SUBSTRING
+TO_LOWER
+TO_UPPER
+TRIM
### Date-time functions
-- AUTO_BUCKET
-- DATE_EXTRACT
-- DATE_FORMAT
-- DATE_PARSE
-- DATE_TRUNC
-- NOW
+BUCKET
+DATE_DIFF
+DATE_EXTRACT
+DATE_FORMAT
+DATE_PARSE
+DATE_TRUNC
+NOW
### Type conversion functions
-- TO_BOOLEAN
-- TO_DATETIME
-- TO_DEGREES
-- TO_DOUBLE
-- TO_INTEGER
-- TO_IP
-- TO_LONG
-- TO_RADIANS
-- TO_STRING
-- TO_UNSIGNED_LONG
-- TO_VERSION
+TO_BOOLEAN
+TO_CARTESIANPOINT
+TO_CARTESIANSHAPE
+TO_DATETIME
+TO_DEGREES
+TO_DOUBLE
+TO_GEOPOINT
+TO_GEOSHAPE
+TO_INTEGER
+TO_IP
+TO_LONG
+TO_RADIANS
+TO_STRING
+TO_UNSIGNED_LONG
+TO_VERSION
+
### Conditional functions and expressions
-- CASE
-- COALESCE
-- GREATEST
-- LEAST
+CASE
+COALESCE
+GREATEST
+LEAST
### Multivalue functions
-- MV_AVG
-- MV_CONCAT
-- MV_COUNT
-- MV_DEDUPE
-- MV_MAX
-- MV_MEDIAN
-- MV_MIN
-- MV_SUM
+MV_AVG
+MV_CONCAT
+MV_COUNT
+MV_DEDUPE
+MV_FIRST
+MV_LAST
+MV_MAX
+MV_MEDIAN
+MV_MIN
+MV_SUM
### Operators
-- Binary operators
-- Logical operators
-- IS NULL and IS NOT NULL predicates
-- CIDR_MATCH
-- ENDS_WITH
-- IN
-- IS_FINITE
-- IS_INFINITE
-- IS_NAN
-- LIKE
-- RLIKE
-- STARTS_WITH
+Binary operators
+Unary operators
+Logical operators
+IS NULL and IS NOT NULL predicates
+CIDR_MATCH
+ENDS_WITH
+IN
+LIKE
+RLIKE
+STARTS_WITH
Here are some example queries:
@@ -210,7 +221,7 @@ FROM employees
```esql
FROM employees
| WHERE hire_date >= "1985-01-01T00:00:00Z" AND hire_date < "1986-01-01T00:00:00Z"
-| EVAL bucket = AUTO_BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
+| EVAL bucket = BUCKET(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")
| STATS AVG(salary) BY bucket
| SORT bucket
```
@@ -244,4 +255,12 @@ FROM logs-*
| WHERE @timestamp <= NOW() - 24 hours
| STATS count = COUNT(*) by log.level
| SORT count DESC
-```
\ No newline at end of file
+```
+
+returns all first names for each first letter
+```esql
+FROM employees
+| EVAL first_letter = SUBSTRING(first_name, 0, 1)
+| STATS first_name = MV_SORT(VALUES(first_name)) BY first_letter
+| SORT first_letter
+```
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/common/ui_settings.ts b/x-pack/plugins/observability_solution/observability_ai_assistant_management/common/ui_settings.ts
index c3cfc1db1d1e6..d6a6db84d1084 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/common/ui_settings.ts
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/common/ui_settings.ts
@@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n';
import {
aiAssistantLogsIndexPattern,
aiAssistantResponseLanguage,
+ aiAssistantSimulatedFunctionCalling,
} from '@kbn/observability-ai-assistant-plugin/common';
import {
DEFAULT_LANGUAGE_OPTION,
@@ -64,4 +65,24 @@ export const uiSettings: Record = {
type: 'string',
requiresPageReload: true,
},
+ [aiAssistantSimulatedFunctionCalling]: {
+ category: ['observability'],
+ name: i18n.translate(
+ 'xpack.observabilityAiAssistantManagement.settingsPage.simulatedFunctionCallingLabel',
+ {
+ defaultMessage: 'Simulate function calling',
+ }
+ ),
+ value: false,
+ description: i18n.translate(
+ 'xpack.observabilityAiAssistantManagement.settingsPage.simulatedFunctionCallingDescription',
+ {
+ defaultMessage:
+ '[technical preview] Use simulated function calling. Simulated function calling does not need API support for functions or tools, but it may decrease performance. Simulated function calling is currently always enabled for non-OpenAI connector, regardless of this setting.',
+ }
+ ),
+ schema: schema.boolean(),
+ type: 'boolean',
+ requiresPageReload: true,
+ },
};
diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx
index b635cdc8bbf8d..9621e77b6e675 100644
--- a/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx
+++ b/x-pack/plugins/observability_solution/observability_ai_assistant_management/public/routes/components/settings_tab/ui_settings.tsx
@@ -10,6 +10,7 @@ import { BottomBarActions, useEditableSettings } from '@kbn/observability-shared
import {
aiAssistantLogsIndexPattern,
aiAssistantResponseLanguage,
+ aiAssistantSimulatedFunctionCalling,
} from '@kbn/observability-ai-assistant-plugin/public';
import { FieldRow, FieldRowProvider } from '@kbn/management-settings-components-field-row';
import { EuiSpacer } from '@elastic/eui';
@@ -17,7 +18,11 @@ import { isEmpty } from 'lodash';
import { i18n } from '@kbn/i18n';
import { useAppContext } from '../../../hooks/use_app_context';
-const settingsKeys = [aiAssistantLogsIndexPattern, aiAssistantResponseLanguage];
+const settingsKeys = [
+ aiAssistantLogsIndexPattern,
+ aiAssistantResponseLanguage,
+ aiAssistantSimulatedFunctionCalling,
+];
export function UISettings() {
const { docLinks, settings, notifications } = useAppContext();
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/experimental_onboarding_flow.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/experimental_onboarding_flow.tsx
index 13d0783c27fac..b27d27b538a04 100644
--- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/experimental_onboarding_flow.tsx
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/experimental_onboarding_flow.tsx
@@ -6,21 +6,21 @@
*/
import { i18n } from '@kbn/i18n';
-
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
+import { useHistory } from 'react-router-dom';
+import { Route, Routes } from '@kbn/shared-ux-router';
+import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
+import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
import {
- EuiPageTemplate,
- EuiSpacer,
EuiButton,
+ EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
- EuiButtonEmpty,
+ EuiPageTemplate,
+ EuiSpacer,
} from '@elastic/eui';
import { css } from '@emotion/react';
-import { useHistory } from 'react-router-dom';
-import { Route, Routes } from '@kbn/shared-ux-router';
-import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
-import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
import backgroundImageUrl from './header/background.svg';
import { Footer } from './footer/footer';
import { OnboardingFlowForm } from './onboarding_flow_form/onboarding_flow_form';
@@ -28,12 +28,14 @@ import { Header } from './header/header';
import { SystemLogsPanel } from './quickstart_flows/system_logs';
import { CustomLogsPanel } from './quickstart_flows/custom_logs';
+const queryClient = new QueryClient();
+
export function ExperimentalOnboardingFlow() {
const history = useHistory();
const location = useLocation();
return (
- <>
+
{/* Test buttons to be removed once integrations selector has been implemented */}
@@ -104,7 +106,7 @@ export function ExperimentalOnboardingFlow() {
- >
+
);
}
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx
index e926705b8692a..84e1ee012edc0 100644
--- a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/onboarding_flow_form.tsx
@@ -6,27 +6,29 @@
*/
import { i18n } from '@kbn/i18n';
-import React from 'react';
+import React, { useCallback, useState } from 'react';
+import { FormattedMessage } from '@kbn/i18n-react';
import type { FunctionComponent } from 'react';
import {
+ EuiAvatar,
EuiCheckableCard,
- EuiTitle,
- EuiText,
- EuiPanel,
- EuiSpacer,
- EuiFlexGrid,
EuiFlexGroup,
EuiFlexItem,
- EuiCard,
- EuiIcon,
- EuiAvatar,
- useEuiTheme,
+ EuiPanel,
+ EuiSpacer,
+ EuiText,
+ EuiTitle,
useGeneratedHtmlId,
} from '@elastic/eui';
+
import { useSearchParams } from 'react-router-dom-v5-compat';
+import { OnboardingFlowPackageList } from '../packages_list';
+import { useCustomMargin } from '../shared/use_custom_margin';
+import { Category } from './types';
+import { useCustomCardsForCategory } from './use_custom_cards_for_category';
interface UseCaseOption {
- id: string;
+ id: Category;
label: string;
description: React.ReactNode;
}
@@ -77,11 +79,31 @@ export const OnboardingFlowForm: FunctionComponent = () => {
},
];
+ const customMargin = useCustomMargin();
const radioGroupId = useGeneratedHtmlId({ prefix: 'onboardingCategory' });
- const { euiTheme } = useEuiTheme();
-
const [searchParams, setSearchParams] = useSearchParams();
+ const packageListSearchBarRef = React.useRef(null);
+ const [integrationSearch, setIntegrationSearch] = useState('');
+
+ const createCollectionCardHandler = useCallback(
+ (query: string) => () => {
+ setIntegrationSearch(query);
+ if (packageListSearchBarRef.current) {
+ packageListSearchBarRef.current.focus();
+ packageListSearchBarRef.current.scrollIntoView({
+ behavior: 'auto',
+ block: 'center',
+ });
+ }
+ },
+ [setIntegrationSearch]
+ );
+
+ const customCards = useCustomCardsForCategory(
+ createCollectionCardHandler,
+ searchParams.get('category') as Category | null
+ );
return (
@@ -96,12 +118,8 @@ export const OnboardingFlowForm: FunctionComponent = () => {
)}
/>
-
- {options.map((option, index) => (
+
+ {options.map((option) => (
{
/>
- {/* Mock integrations grid */}
-
- {new Array(6).fill(null).map((_, index) => (
- }
- betaBadgeProps={
- index === 0
- ? {
- label: 'Quick Start',
- color: 'accent',
- size: 's',
- }
- : undefined
- }
- hasBorder
- css={{
- borderColor: index === 0 ? '#ba3d76' : undefined,
- }}
- />
- ))}
-
+ {Array.isArray(customCards) && (
+
+ )}
+
+
+
+
+
>
)}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/index.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/types.ts
similarity index 83%
rename from x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/index.ts
rename to x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/types.ts
index c89414639b417..37c3cf3d025d0 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/index.ts
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/types.ts
@@ -5,4 +5,4 @@
* 2.0.
*/
-export * from './total_users';
+export type Category = 'apm' | 'infra' | 'logs';
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts
new file mode 100644
index 0000000000000..f1ba6ad17b424
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/onboarding_flow_form/use_custom_cards_for_category.ts
@@ -0,0 +1,212 @@
+/*
+ * 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 {
+ reactRouterNavigate,
+ useKibana,
+} from '@kbn/kibana-react-plugin/public';
+import { useHistory } from 'react-router-dom';
+import { useLocation } from 'react-router-dom-v5-compat';
+import { CustomCard, FeaturedCard } from '../packages_list/types';
+import { Category } from './types';
+
+function toFeaturedCard(name: string): FeaturedCard {
+ return { type: 'featured', name };
+}
+
+export function useCustomCardsForCategory(
+ createCollectionCardHandler: (query: string) => () => void,
+ category: Category | null
+): CustomCard[] | undefined {
+ const history = useHistory();
+ const location = useLocation();
+ const getUrlForApp = useKibana()?.services.application?.getUrlForApp;
+
+ const { href: systemLogsUrl } = reactRouterNavigate(
+ history,
+ `/systemLogs/${location.search}`
+ );
+ const { href: customLogsUrl } = reactRouterNavigate(
+ history,
+ `/customLogs/${location.search}`
+ );
+
+ switch (category) {
+ case 'apm':
+ return [
+ {
+ id: 'apm-generated',
+ type: 'generated',
+ title: 'Elastic APM',
+ description:
+ 'Collect distributed traces from your applications with Elastic APM',
+ name: 'apm',
+ categories: ['observability'],
+ icons: [
+ {
+ type: 'eui',
+ src: 'apmApp',
+ },
+ ],
+ url: getUrlForApp?.('apm') ?? '',
+ version: '',
+ integration: '',
+ },
+ {
+ id: 'synthetics-generated',
+ type: 'generated',
+ title: 'Synthetic monitor',
+ description: 'Monitor endpoints, pages, and user journeys',
+ name: 'synthetics',
+ categories: ['observability'],
+ icons: [
+ {
+ type: 'eui',
+ src: 'logoUptime',
+ },
+ ],
+ url: getUrlForApp?.('synthetics') ?? '',
+ version: '',
+ integration: '',
+ },
+ ];
+ case 'infra':
+ return [
+ toFeaturedCard('kubernetes'),
+ toFeaturedCard('prometheus'),
+ toFeaturedCard('docker'),
+ {
+ id: 'azure-generated',
+ type: 'generated',
+ title: 'Azure',
+ description: 'Collect logs and metrics from Microsoft Azure',
+ name: 'azure',
+ categories: ['observability'],
+ icons: [],
+ url: 'https://azure.com',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('azure'),
+ },
+ {
+ id: 'aws-generated',
+ type: 'generated',
+ title: 'AWS',
+ description:
+ 'Collect logs and metrics from Amazon Web Services (AWS)',
+ name: 'aws',
+ categories: ['observability'],
+ icons: [],
+ url: 'https://aws.com',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('aws'),
+ },
+ {
+ id: 'gcp-generated',
+ type: 'generated',
+ title: 'Google Cloud Platform',
+ description: 'Collect logs and metrics from Google Cloud Platform',
+ name: 'gcp',
+ categories: ['observability'],
+ icons: [],
+ url: '',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('gcp'),
+ },
+ ];
+ case 'logs':
+ return [
+ {
+ id: 'system-logs',
+ type: 'generated',
+ title: 'Stream host system logs',
+ description:
+ 'The quickest path to onboard log data from your own machine or server',
+ name: 'system-logs-generated',
+ categories: ['observability'],
+ icons: [
+ {
+ type: 'svg',
+ src: '/XXXXXXXXXXXX/plugins/home/assets/logos/system.svg',
+ },
+ ],
+ url: systemLogsUrl,
+ version: '',
+ integration: '',
+ },
+ {
+ id: 'logs-logs',
+ type: 'generated',
+ title: 'Stream log files',
+ description:
+ 'Stream any logs into Elastic in a simple way and explore their data',
+ name: 'logs-logs-generated',
+ categories: ['observability'],
+ icons: [
+ {
+ type: 'eui',
+ src: 'filebeatApp',
+ },
+ ],
+ url: customLogsUrl,
+ version: '',
+ integration: '',
+ },
+ toFeaturedCard('nginx'),
+ {
+ id: 'azure-logs-generated',
+ type: 'generated',
+ title: 'Azure',
+ description: 'Collect logs from Microsoft Azure',
+ name: 'azure',
+ categories: ['observability'],
+ icons: [],
+ url: 'https://azure.com',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('azure'),
+ },
+ {
+ id: 'aws-logs-generated',
+ type: 'generated',
+ title: 'AWS',
+ description: 'Collect logs from Amazon Web Services (AWS)',
+ name: 'aws',
+ categories: ['observability'],
+ icons: [],
+ url: 'https://aws.com',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('aws'),
+ },
+ {
+ id: 'gcp-logs-generated',
+ type: 'generated',
+ title: 'Google Cloud Platform',
+ description: 'Collect logs from Google Cloud Platform',
+ name: 'gcp',
+ categories: ['observability'],
+ icons: [],
+ url: '',
+ version: '',
+ integration: '',
+ isCollectionCard: true,
+ onCardClick: createCollectionCardHandler('gcp'),
+ },
+ ];
+
+ default:
+ return undefined;
+ }
+}
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/index.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/index.tsx
new file mode 100644
index 0000000000000..67bad02abddb6
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/index.tsx
@@ -0,0 +1,190 @@
+/*
+ * 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 {
+ AvailablePackagesHookType,
+ IntegrationCardItem,
+} from '@kbn/fleet-plugin/public';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+import {
+ EuiButton,
+ EuiCallOut,
+ EuiSearchBar,
+ EuiSkeletonText,
+} from '@elastic/eui';
+import { css } from '@emotion/react';
+import React, { useRef, Suspense, useState } from 'react';
+import useAsyncRetry from 'react-use/lib/useAsyncRetry';
+import { PackageList, fetchAvailablePackagesHook } from './lazy';
+import { useIntegrationCardList } from './use_integration_card_list';
+import { useCustomMargin } from '../shared/use_custom_margin';
+import { CustomCard } from './types';
+
+interface Props {
+ /**
+ * A subset of either existing card names to feature, or generated
+ * cards to display. The inclusion of CustomCards will override the default
+ * list functionality.
+ */
+ customCards?: CustomCard[];
+ /**
+ * Override the default `observability` option.
+ */
+ selectedCategory?: string;
+ showSearchBar?: boolean;
+ searchBarRef?: React.Ref;
+ searchQuery?: string;
+ setSearchQuery?: React.Dispatch>;
+}
+
+type WrapperProps = Props & {
+ useAvailablePackages: AvailablePackagesHookType;
+};
+
+const Loading = () => ;
+
+const PackageListGridWrapper = ({
+ selectedCategory = 'observability',
+ useAvailablePackages,
+ showSearchBar = false,
+ searchBarRef,
+ searchQuery,
+ setSearchQuery,
+ customCards,
+}: WrapperProps) => {
+ const [isInitialHidden, setIsInitialHidden] = useState(showSearchBar);
+ const customMargin = useCustomMargin();
+ const { filteredCards, isLoading } = useAvailablePackages({
+ prereleaseIntegrationsEnabled: false,
+ });
+
+ const list: IntegrationCardItem[] = useIntegrationCardList(
+ filteredCards,
+ selectedCategory,
+ customCards
+ );
+
+ React.useEffect(() => {
+ if (isInitialHidden && searchQuery) {
+ setIsInitialHidden(false);
+ }
+ }, [searchQuery, isInitialHidden]);
+
+ if (!isInitialHidden && isLoading) return ;
+
+ const showPackageList =
+ (showSearchBar && !isInitialHidden) || showSearchBar === false;
+
+ return (
+ }>
+
+ {showSearchBar && (
+
+ {
+ (
+ searchBarRef as React.MutableRefObject
+ ).current = ref;
+ },
+ }}
+ onChange={(arg) => {
+ if (setSearchQuery) {
+ setSearchQuery(arg.queryText);
+ }
+ setIsInitialHidden(false);
+ }}
+ query={searchQuery}
+ />
+
+ )}
+ {showPackageList && (
+
{}}
+ setCategory={() => {}}
+ categories={[]}
+ setUrlandReplaceHistory={() => {}}
+ setUrlandPushHistory={() => {}}
+ />
+ )}
+
+
+ );
+};
+
+const WithAvailablePackages = React.forwardRef(
+ (props: Props, searchBarRef?: React.Ref) => {
+ const ref = useRef(null);
+
+ const {
+ error: errorLoading,
+ retry: retryAsyncLoad,
+ loading: asyncLoading,
+ } = useAsyncRetry(async () => {
+ ref.current = await fetchAvailablePackagesHook();
+ });
+
+ if (errorLoading)
+ return (
+
+
+
+
+ {
+ if (!asyncLoading) retryAsyncLoad();
+ }}
+ >
+
+
+
+ );
+
+ if (asyncLoading || ref.current === null) return ;
+
+ return (
+
+ );
+ }
+);
+
+export { WithAvailablePackages as OnboardingFlowPackageList };
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/lazy.tsx b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/lazy.tsx
new file mode 100644
index 0000000000000..84d8504c7729c
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/lazy.tsx
@@ -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 { AvailablePackagesHookType } from '@kbn/fleet-plugin/public';
+import { lazy } from 'react';
+
+export const PackageList = lazy(async () => ({
+ default: await import('@kbn/fleet-plugin/public')
+ .then((module) => module.PackageList())
+ .then((pkg) => pkg.PackageListGrid),
+}));
+
+export const fetchAvailablePackagesHook =
+ (): Promise =>
+ import('@kbn/fleet-plugin/public')
+ .then((module) => module.AvailablePackagesHook())
+ .then((hook) => hook.useAvailablePackages);
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/types.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/types.ts
new file mode 100644
index 0000000000000..31b5ed945be45
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/types.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { IntegrationCardItem } from '@kbn/fleet-plugin/public';
+
+export type GeneratedCard = {
+ type: 'generated';
+} & IntegrationCardItem;
+
+export interface FeaturedCard {
+ type: 'featured';
+ name: string;
+}
+
+export type CustomCard = FeaturedCard | GeneratedCard;
diff --git a/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/use_integration_card_list.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/use_integration_card_list.ts
new file mode 100644
index 0000000000000..d87b04cf7902c
--- /dev/null
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/packages_list/use_integration_card_list.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 { IntegrationCardItem } from '@kbn/fleet-plugin/public';
+import { CustomCard } from './types';
+
+const QUICKSTART_FLOWS = ['kubernetes', 'nginx', 'system-logs-generated'];
+
+const toCustomCard = (card: IntegrationCardItem) => ({
+ ...card,
+ isQuickstart: QUICKSTART_FLOWS.includes(card.name),
+});
+
+function extractFeaturedCards(
+ filteredCards: IntegrationCardItem[],
+ featuredCardNames?: string[]
+) {
+ const featuredCards: Record = {};
+ filteredCards.forEach((card) => {
+ if (featuredCardNames?.includes(card.name)) {
+ featuredCards[card.name] = card;
+ }
+ });
+ return featuredCards;
+}
+
+export function useIntegrationCardList(
+ filteredCards: IntegrationCardItem[],
+ selectedCategory = 'observability',
+ customCards?: CustomCard[]
+): IntegrationCardItem[] {
+ const featuredCards = useMemo(() => {
+ if (!customCards) return {};
+ return extractFeaturedCards(
+ filteredCards,
+ customCards.filter((c) => c.type === 'featured').map((c) => c.name)
+ );
+ }, [filteredCards, customCards]);
+
+ if (customCards && customCards.length > 0) {
+ return customCards
+ .map((c) => {
+ if (c.type === 'featured') {
+ return !!featuredCards[c.name]
+ ? toCustomCard(featuredCards[c.name]!)
+ : null;
+ }
+ return toCustomCard(c);
+ })
+ .filter((c) => c) as IntegrationCardItem[];
+ }
+ return filteredCards
+ .filter((card) => card.categories.includes(selectedCategory))
+ .map(toCustomCard);
+}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/alerts/index.ts b/x-pack/plugins/observability_solution/observability_onboarding/public/application/shared/use_custom_margin.ts
similarity index 59%
rename from x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/alerts/index.ts
rename to x-pack/plugins/observability_solution/observability_onboarding/public/application/shared/use_custom_margin.ts
index 7780a5273b596..4c39d419c188a 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/alerts/index.ts
+++ b/x-pack/plugins/observability_solution/observability_onboarding/public/application/shared/use_custom_margin.ts
@@ -5,12 +5,9 @@
* 2.0.
*/
-import type { HistogramBucket } from '../common';
+import { useEuiTheme } from '@elastic/eui';
-export interface AlertsGroupData {
- key: string;
- doc_count: number;
- alerts: {
- buckets: HistogramBucket[];
- };
+export function useCustomMargin() {
+ const { euiTheme } = useEuiTheme();
+ return { margin: `calc(${euiTheme.size.xxl} / 2)` };
}
diff --git a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts
index bdb648a32a0e2..6b847fb396967 100644
--- a/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts
+++ b/x-pack/plugins/osquery/cypress/e2e/all/add_integration.cy.ts
@@ -39,10 +39,7 @@ import {
} from '../../tasks/integrations';
import { ServerlessRoleName } from '../../support/roles';
-// Failing: See https://github.com/elastic/kibana/issues/170445
-// Failing: See https://github.com/elastic/kibana/issues/169701
-// Failing: See https://github.com/elastic/kibana/issues/170593
-describe.skip('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () => {
+describe('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () => {
let savedQueryId: string;
before(() => {
@@ -77,8 +74,7 @@ describe.skip('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () =>
}
);
- // FLAKY: https://github.com/elastic/kibana/issues/169701
- describe.skip('Add and upgrade integration', { tags: ['@ess', '@serverless'] }, () => {
+ describe('Add and upgrade integration', { tags: ['@ess', '@serverless'] }, () => {
const oldVersion = '0.7.4';
const [integrationName, policyName] = generateRandomStringName(2);
let policyId: string;
@@ -107,8 +103,7 @@ describe.skip('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () =>
cy.contains(`version: ${oldVersion}`).should('not.exist');
});
});
- // FLAKY: https://github.com/elastic/kibana/issues/170593
- describe.skip('Add integration to policy', () => {
+ describe('Add integration to policy', () => {
const [integrationName, policyName] = generateRandomStringName(2);
let policyId: string;
beforeEach(() => {
@@ -148,8 +143,7 @@ describe.skip('ALL - Add Integration', { tags: ['@ess', '@serverless'] }, () =>
});
});
- // FLAKY: https://github.com/elastic/kibana/issues/170445
- describe.skip('Upgrade policy with existing packs', () => {
+ describe('Upgrade policy with existing packs', () => {
const oldVersion = '1.2.0';
const [policyName, integrationName, packName] = generateRandomStringName(3);
let policyId: string;
diff --git a/x-pack/plugins/osquery/cypress/tasks/integrations.ts b/x-pack/plugins/osquery/cypress/tasks/integrations.ts
index b4c1049ffc7f6..e4c38854a5faf 100644
--- a/x-pack/plugins/osquery/cypress/tasks/integrations.ts
+++ b/x-pack/plugins/osquery/cypress/tasks/integrations.ts
@@ -162,9 +162,9 @@ export const installPackageWithVersion = (integration: string, version: string)
};
const extractSemanticVersion = (str: string) => {
- const match = str.match(/(\d+\.\d+\.\d+)/);
+ const match = str.match(/(Managerv\d+\.\d+\.\d+)/);
if (match && match[1]) {
- return match[1];
+ return match[1].replace('Managerv', '');
} else {
return null; // Return null if no match found
}
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/hosts.ts b/x-pack/plugins/security_solution/common/api/search_strategy/hosts/hosts.ts
index a5931bd42972e..7be1b0d4d6c0d 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/hosts.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/hosts/hosts.ts
@@ -12,7 +12,3 @@ export * from './details';
export * from './overview';
export * from './uncommon_processes';
-
-export * from './kpi_hosts';
-
-export * from './kpi_unique_ips';
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_hosts.ts b/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_hosts.ts
deleted file mode 100644
index e49741efe0d24..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_hosts.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { HostsKpiQueries } from '../model/factory_query_type';
-import { pagination } from '../model/pagination';
-import { requestBasicOptionsSchema } from '../model/request_basic_options';
-import { timerange } from '../model/timerange';
-import { sort } from './model/sort';
-
-export const kpiHostsSchema = requestBasicOptionsSchema.extend({
- sort,
- pagination,
- timerange,
- factoryQueryType: z.literal(HostsKpiQueries.kpiHosts),
-});
-
-export type KpiHostsRequestOptionsInput = z.input;
-
-export type KpiHostsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_unique_ips.ts b/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_unique_ips.ts
deleted file mode 100644
index 998b6a076bd9a..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/hosts/kpi_unique_ips.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { HostsKpiQueries } from '../model/factory_query_type';
-import { pagination } from '../model/pagination';
-import { requestBasicOptionsSchema } from '../model/request_basic_options';
-import { timerange } from '../model/timerange';
-import { sort } from './model/sort';
-
-export const kpiUniqueIpsSchema = requestBasicOptionsSchema.extend({
- sort,
- pagination,
- timerange,
- factoryQueryType: z.literal(HostsKpiQueries.kpiUniqueIps),
-});
-
-export type KpiUniqueIpsRequestOptionsInput = z.input;
-
-export type KpiUniqueIpsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/index.ts b/x-pack/plugins/security_solution/common/api/search_strategy/index.ts
index 7282cb9e09ff3..2cc95948b8a17 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/index.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/index.ts
@@ -18,20 +18,11 @@ import {
hostDetailsSchema,
hostOverviewSchema,
hostUncommonProcessesSchema,
- kpiHostsSchema,
- kpiUniqueIpsSchema,
} from './hosts/hosts';
-import { matrixHistogramSchema } from './matrix_histogram/matrix_histogram';
import { networkDetailsSchema } from './network/details';
import { networkDnsSchema } from './network/dns';
import { networkHttpSchema } from './network/http';
-import {
- networkKpiDns,
- networkKpiEvents,
- networkKpiTlsHandshakes,
- networkKpiUniqueFlows,
- networkKpiUniquePrivateIps,
-} from './network/kpi';
+
import { networkOverviewSchema } from './network/overview';
import { networkTlsSchema } from './network/tls';
import { networkTopCountriesSchema } from './network/top_countries';
@@ -50,10 +41,8 @@ import {
} from './risk_score/risk_score';
import {
- authenticationsKpiSchema,
managedUserDetailsSchema,
observedUserDetailsSchema,
- totalUsersKpiSchema,
userAuthenticationsSchema,
usersSchema,
} from './users/users';
@@ -64,8 +53,6 @@ export * from './hosts/hosts';
export * from './users/users';
-export * from './matrix_histogram/matrix_histogram';
-
export * from './network/network';
export * from './related_entities/related_entities';
@@ -84,15 +71,11 @@ export const searchStrategyRequestSchema = z.discriminatedUnion('factoryQueryTyp
firstLastSeenRequestOptionsSchema,
allHostsSchema,
hostDetailsSchema,
- kpiHostsSchema,
- kpiUniqueIpsSchema,
hostOverviewSchema,
hostUncommonProcessesSchema,
usersSchema,
observedUserDetailsSchema,
managedUserDetailsSchema,
- totalUsersKpiSchema,
- authenticationsKpiSchema,
userAuthenticationsSchema,
hostsRiskScoreRequestOptionsSchema,
usersRiskScoreRequestOptionsSchema,
@@ -108,12 +91,6 @@ export const searchStrategyRequestSchema = z.discriminatedUnion('factoryQueryTyp
networkTopNFlowSchema,
networkTopNFlowCountSchema,
networkUsersSchema,
- networkKpiDns,
- networkKpiEvents,
- networkKpiTlsHandshakes,
- networkKpiUniqueFlows,
- networkKpiUniquePrivateIps,
- matrixHistogramSchema,
threatIntelSourceRequestOptionsSchema,
eventEnrichmentRequestOptionsSchema,
]);
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/matrix_histogram/matrix_histogram.ts b/x-pack/plugins/security_solution/common/api/search_strategy/matrix_histogram/matrix_histogram.ts
index 7ad60f8d8b6fc..35152b5d17697 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/matrix_histogram/matrix_histogram.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/matrix_histogram/matrix_histogram.ts
@@ -5,13 +5,6 @@
* 2.0.
*/
-import { z } from 'zod';
-import { MatrixHistogramQuery } from '../model/factory_query_type';
-import { inspect } from '../model/inspect';
-import { requestBasicOptionsSchema } from '../model/request_basic_options';
-import { runtimeMappings } from '../model/runtime_mappings';
-import { timerange } from '../model/timerange';
-
export enum MatrixHistogramType {
authentications = 'authentications',
anomalies = 'anomalies',
@@ -20,37 +13,3 @@ export enum MatrixHistogramType {
dns = 'dns',
preview = 'preview',
}
-
-export const matrixHistogramSchema = requestBasicOptionsSchema.extend({
- histogramType: z.enum([
- MatrixHistogramType.alerts,
- MatrixHistogramType.anomalies,
- MatrixHistogramType.authentications,
- MatrixHistogramType.dns,
- MatrixHistogramType.events,
- MatrixHistogramType.preview,
- ]),
- stackByField: z.string().optional(),
- threshold: z
- .object({
- field: z.array(z.string()),
- value: z.string(),
- cardinality: z
- .object({
- field: z.array(z.string()),
- value: z.string(),
- })
- .optional(),
- })
- .optional(),
- inspect,
- isPtrIncluded: z.boolean().default(false),
- includeMissingData: z.boolean().default(true),
- runtimeMappings,
- timerange,
- factoryQueryType: z.literal(MatrixHistogramQuery),
-});
-
-export type MatrixHistogramRequestOptionsInput = z.input;
-
-export type MatrixHistogramRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/model/factory_query_type.ts b/x-pack/plugins/security_solution/common/api/search_strategy/model/factory_query_type.ts
index 38de7349f1311..83a924f89253b 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/model/factory_query_type.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/model/factory_query_type.ts
@@ -12,26 +12,11 @@ export enum HostsQueries {
uncommonProcesses = 'uncommonProcesses',
}
-export enum NetworkKpiQueries {
- dns = 'networkKpiDns',
- networkEvents = 'networkKpiNetworkEvents',
- tlsHandshakes = 'networkKpiTlsHandshakes',
- uniqueFlows = 'networkKpiUniqueFlows',
- uniquePrivateIps = 'networkKpiUniquePrivateIps',
-}
-
-export enum HostsKpiQueries {
- kpiHosts = 'hostsKpiHosts',
- kpiUniqueIps = 'hostsKpiUniqueIps',
-}
-
export enum UsersQueries {
observedDetails = 'observedUserDetails',
managedDetails = 'managedUserDetails',
- kpiTotalUsers = 'usersKpiTotalUsers',
users = 'allUsers',
authentications = 'authentications',
- kpiAuthentications = 'usersKpiAuthentications',
}
export enum NetworkQueries {
@@ -57,8 +42,6 @@ export enum CtiQueries {
dataSource = 'dataSource',
}
-export const MatrixHistogramQuery = 'matrixHistogram';
-
export const FirstLastSeenQuery = 'firstlastseen';
export enum RelatedEntitiesQueries {
@@ -68,12 +51,9 @@ export enum RelatedEntitiesQueries {
export type FactoryQueryTypes =
| HostsQueries
- | HostsKpiQueries
| UsersQueries
| NetworkQueries
- | NetworkKpiQueries
| RiskQueries
| CtiQueries
- | typeof MatrixHistogramQuery
| typeof FirstLastSeenQuery
| RelatedEntitiesQueries;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/dns.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/dns.ts
deleted file mode 100644
index fd614dd76e224..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/dns.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { NetworkKpiQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const networkKpiDns = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(NetworkKpiQueries.dns),
-});
-
-export type NetworkKpiDnsRequestOptionsInput = z.input;
-
-export type NetworkKpiDnsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/events.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/events.ts
deleted file mode 100644
index 7aef866065f29..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/events.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { NetworkKpiQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const networkKpiEvents = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(NetworkKpiQueries.networkEvents),
-});
-
-export type NetworkKpiEventsRequestOptionsInput = z.input;
-
-export type NetworkKpiEventsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/index.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/index.ts
deleted file mode 100644
index 2fce614035fd6..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export * from './dns';
-
-export * from './events';
-
-export * from './tls_handshakes';
-
-export * from './unique_flows';
-
-export * from './unique_private_ips';
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/tls_handshakes.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/tls_handshakes.ts
deleted file mode 100644
index 6847824032390..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/tls_handshakes.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { NetworkKpiQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const networkKpiTlsHandshakes = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(NetworkKpiQueries.tlsHandshakes),
-});
-
-export type NetworkKpiTlsHandshakesRequestOptionsInput = z.input;
-
-export type NetworkKpiTlsHandshakesRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_flows.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_flows.ts
deleted file mode 100644
index 2ecc2f9d699de..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_flows.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { NetworkKpiQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const networkKpiUniqueFlows = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(NetworkKpiQueries.uniqueFlows),
-});
-
-export type NetworkKpiUniqueFlowsRequestOptionsInput = z.input;
-
-export type NetworkKpiUniqueFlowsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_private_ips.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_private_ips.ts
deleted file mode 100644
index f870cd921c308..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/kpi/unique_private_ips.ts
+++ /dev/null
@@ -1,23 +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 { z } from 'zod';
-import { NetworkKpiQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const networkKpiUniquePrivateIps = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(NetworkKpiQueries.uniquePrivateIps),
-});
-
-export type NetworkKpiUniquePrivateIpsRequestOptionsInput = z.input<
- typeof networkKpiUniquePrivateIps
->;
-
-export type NetworkKpiUniquePrivateIpsRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/network/network.ts b/x-pack/plugins/security_solution/common/api/search_strategy/network/network.ts
index 82cb6f78e8875..b9b90ba90b16b 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/network/network.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/network/network.ts
@@ -11,8 +11,6 @@ export * from './dns';
export * from './http';
-export * from './kpi';
-
export * from './overview';
export * from './tls';
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/authentications.ts b/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/authentications.ts
deleted file mode 100644
index 42919d8c5111f..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/authentications.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { UsersQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const authenticationsKpiSchema = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(UsersQueries.kpiAuthentications),
-});
-
-export type AuthenticationsKpiRequestOptionsInput = z.input;
-
-export type AuthenticationsKpiRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/total_users.ts b/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/total_users.ts
deleted file mode 100644
index 3822845cc58ac..0000000000000
--- a/x-pack/plugins/security_solution/common/api/search_strategy/users/kpi/total_users.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { z } from 'zod';
-import { UsersQueries } from '../../model/factory_query_type';
-
-import { requestBasicOptionsSchema } from '../../model/request_basic_options';
-import { timerange } from '../../model/timerange';
-
-export const totalUsersKpiSchema = requestBasicOptionsSchema.extend({
- timerange,
- factoryQueryType: z.literal(UsersQueries.kpiTotalUsers),
-});
-
-export type TotalUsersKpiRequestOptionsInput = z.input;
-
-export type TotalUsersKpiRequestOptions = z.infer;
diff --git a/x-pack/plugins/security_solution/common/api/search_strategy/users/users.ts b/x-pack/plugins/security_solution/common/api/search_strategy/users/users.ts
index 198af6ad7703f..5997117a542ae 100644
--- a/x-pack/plugins/security_solution/common/api/search_strategy/users/users.ts
+++ b/x-pack/plugins/security_solution/common/api/search_strategy/users/users.ts
@@ -9,10 +9,6 @@ export * from './observed_details';
export * from './managed_details';
-export * from './kpi/total_users';
-
-export * from './kpi/authentications';
-
export * from './all';
export * from './authentications';
diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts
index d1e6ed3b9785b..ada9697547995 100644
--- a/x-pack/plugins/security_solution/common/constants.ts
+++ b/x-pack/plugins/security_solution/common/constants.ts
@@ -10,7 +10,6 @@ import type { AddOptionsListControlProps } from '@kbn/controls-plugin/public';
import * as i18n from './translations';
export { SecurityPageName } from '@kbn/security-solution-navigation';
-
/**
* as const
*
@@ -247,44 +246,7 @@ export const DETECTION_ENGINE_RULES_BULK_CREATE =
export const DETECTION_ENGINE_RULES_BULK_UPDATE =
`${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const;
-/**
- * Internal Risk Score routes
- */
-export const INTERNAL_RISK_SCORE_URL = '/internal/risk_score' as const;
-export const DEV_TOOL_PREBUILT_CONTENT =
- `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/{console_id}` as const;
-export const devToolPrebuiltContentUrl = (spaceId: string, consoleId: string) =>
- `/s/${spaceId}${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/${consoleId}` as const;
-export const PREBUILT_SAVED_OBJECTS_BULK_CREATE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/{template_name}`;
-export const prebuiltSavedObjectsBulkCreateUrl = (templateName: string) =>
- `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/${templateName}` as const;
-export const PREBUILT_SAVED_OBJECTS_BULK_DELETE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/{template_name}`;
-export const prebuiltSavedObjectsBulkDeleteUrl = (templateName: string) =>
- `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/${templateName}` as const;
-export const RISK_SCORE_CREATE_INDEX = `${INTERNAL_RISK_SCORE_URL}/indices/create`;
-export const RISK_SCORE_DELETE_INDICES = `${INTERNAL_RISK_SCORE_URL}/indices/delete`;
-export const RISK_SCORE_CREATE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/create`;
-export const RISK_SCORE_DELETE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/delete`;
-export const RISK_SCORE_PREVIEW_URL = `${INTERNAL_RISK_SCORE_URL}/preview`;
-
-export const RISK_ENGINE_URL = `${INTERNAL_RISK_SCORE_URL}/engine`;
-export const RISK_ENGINE_STATUS_URL = `${RISK_ENGINE_URL}/status`;
-export const RISK_ENGINE_INIT_URL = `${RISK_ENGINE_URL}/init`;
-export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable`;
-export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable`;
-export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges`;
-export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings`;
-
-export const ASSET_CRITICALITY_URL = `/internal/asset_criticality`;
-export const ASSET_CRITICALITY_PRIVILEGES_URL = `/internal/asset_criticality/privileges`;
-export const ASSET_CRITICALITY_STATUS_URL = `${ASSET_CRITICALITY_URL}/status`;
-export const ASSET_CRITICALITY_CSV_UPLOAD_URL = `${ASSET_CRITICALITY_URL}/upload_csv`;
-
-/**
- * Public Risk Score routes
- */
-export const RISK_ENGINE_PUBLIC_PREFIX = '/api/risk_scores' as const;
-export const RISK_SCORE_CALCULATION_URL = `${RISK_ENGINE_PUBLIC_PREFIX}/calculation` as const;
+export * from './entity_analytics/constants';
export const INTERNAL_DASHBOARDS_URL = `/internal/dashboards` as const;
export const INTERNAL_TAGS_URL = `/internal/tags`;
diff --git a/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts
index 6e0669c985f5a..6fd4bb824efdf 100644
--- a/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts
+++ b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts
@@ -5,6 +5,11 @@
* 2.0.
*/
+export const ASSET_CRITICALITY_URL = `/internal/asset_criticality`;
+export const ASSET_CRITICALITY_PRIVILEGES_URL = `/internal/asset_criticality/privileges`;
+export const ASSET_CRITICALITY_STATUS_URL = `${ASSET_CRITICALITY_URL}/status`;
+export const ASSET_CRITICALITY_CSV_UPLOAD_URL = `${ASSET_CRITICALITY_URL}/upload_csv`;
+
export const ASSET_CRITICALITY_INDEX_PATTERN = '.asset-criticality.asset-criticality-*';
type AssetCriticalityIndexPrivilege = 'read' | 'write';
diff --git a/x-pack/plugins/security_solution/common/entity_analytics/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/constants.ts
new file mode 100644
index 0000000000000..08ee2d6afd517
--- /dev/null
+++ b/x-pack/plugins/security_solution/common/entity_analytics/constants.ts
@@ -0,0 +1,10 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+export * from './asset_criticality/constants';
+export * from './risk_engine/constants';
+export * from './risk_score/constants';
diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts
index d876e2ebae11b..dc23dceadd642 100644
--- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts
+++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts
@@ -4,6 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
+import { INTERNAL_RISK_SCORE_URL } from '../risk_score/constants';
+export const RISK_ENGINE_URL = `${INTERNAL_RISK_SCORE_URL}/engine`;
+export const RISK_ENGINE_STATUS_URL = `${RISK_ENGINE_URL}/status`;
+export const RISK_ENGINE_INIT_URL = `${RISK_ENGINE_URL}/init`;
+export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable`;
+export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable`;
+export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges`;
+export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings`;
+
export const MAX_SPACES_COUNT = 1;
type ClusterPrivilege = 'manage_index_templates' | 'manage_transform';
diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts
index d86ddf4625372..c496cdab418b5 100644
--- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts
+++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts
@@ -27,13 +27,14 @@ export interface InitRiskEngineResult {
errors: string[];
}
-export interface SimpleRiskInput {
+export interface EntityRiskInput {
id: string;
index: string;
category: RiskCategories;
description: string;
risk_score: string | number | undefined;
timestamp: string | undefined;
+ contribution_score?: number;
}
export interface EcsRiskScore {
@@ -48,7 +49,7 @@ export interface EcsRiskScore {
};
}
-export type RiskInputs = SimpleRiskInput[];
+export type RiskInputs = EntityRiskInput[];
/**
* The API response object representing a risk score
diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_score/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_score/constants.ts
new file mode 100644
index 0000000000000..0c5648fc537bd
--- /dev/null
+++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_score/constants.ts
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+/**
+ * Public Risk Score routes
+ */
+export const RISK_ENGINE_PUBLIC_PREFIX = '/api/risk_scores' as const;
+export const RISK_SCORE_CALCULATION_URL = `${RISK_ENGINE_PUBLIC_PREFIX}/calculation` as const;
+
+/**
+ * Internal Risk Score routes
+ */
+export const INTERNAL_RISK_SCORE_URL = '/internal/risk_score' as const;
+export const DEV_TOOL_PREBUILT_CONTENT =
+ `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/{console_id}` as const;
+export const devToolPrebuiltContentUrl = (spaceId: string, consoleId: string) =>
+ `/s/${spaceId}${INTERNAL_RISK_SCORE_URL}/prebuilt_content/dev_tool/${consoleId}` as const;
+export const PREBUILT_SAVED_OBJECTS_BULK_CREATE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/{template_name}`;
+export const prebuiltSavedObjectsBulkCreateUrl = (templateName: string) =>
+ `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_create/${templateName}` as const;
+export const PREBUILT_SAVED_OBJECTS_BULK_DELETE = `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/{template_name}`;
+export const prebuiltSavedObjectsBulkDeleteUrl = (templateName: string) =>
+ `${INTERNAL_RISK_SCORE_URL}/prebuilt_content/saved_objects/_bulk_delete/${templateName}` as const;
+export const RISK_SCORE_CREATE_INDEX = `${INTERNAL_RISK_SCORE_URL}/indices/create`;
+export const RISK_SCORE_DELETE_INDICES = `${INTERNAL_RISK_SCORE_URL}/indices/delete`;
+export const RISK_SCORE_CREATE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/create`;
+export const RISK_SCORE_DELETE_STORED_SCRIPT = `${INTERNAL_RISK_SCORE_URL}/stored_scripts/delete`;
+export const RISK_SCORE_PREVIEW_URL = `${INTERNAL_RISK_SCORE_URL}/preview`;
diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts
index f4a1f7fd64176..89d40bbb36fb2 100644
--- a/x-pack/plugins/security_solution/common/experimental_features.ts
+++ b/x-pack/plugins/security_solution/common/experimental_features.ts
@@ -19,7 +19,6 @@ export const allowedExperimentalValues = Object.freeze({
excludePoliciesInFilterEnabled: false,
kubernetesEnabled: true,
- chartEmbeddablesEnabled: true,
donutChartEmbeddablesEnabled: false, // Depends on https://github.com/elastic/kibana/issues/136409 item 2 - 6
/**
diff --git a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts
index d73b39ceb282d..f50cdc82f5857 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/common/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/common/index.ts
@@ -19,23 +19,6 @@ export type Maybe = T | null;
export type SearchHit = IEsSearchResponse['rawResponse']['hits']['hits'][0];
-export interface KpiHistogramData {
- x?: Maybe;
- y?: Maybe;
-}
-
-export interface KpiHistogram {
- key_as_string: string;
- key: number;
- doc_count: number;
- count: T;
-}
-
-export interface KpiGeneralHistogramCount {
- value?: number;
- doc_count?: number;
-}
-
export interface PageInfoPaginated {
activePage: number;
fakeTotalCount: number;
@@ -76,3 +59,14 @@ export interface GenericBuckets {
}
export type StringOrNumber = string | number;
+
+export type Fields = Record>>;
+
+export interface EventHit extends SearchHit {
+ sort: string[];
+ _source: EventSource;
+ fields: Fields;
+ aggregations: {
+ [agg: string]: unknown;
+ };
+}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts
index 3643f036ad563..46b6c5306f4b6 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/index.ts
@@ -10,7 +10,6 @@ import { HostsFields } from '../../../api/search_strategy/hosts/model/sort';
export * from './all';
export * from './common';
export * from './details';
-export * from './kpi';
export * from './overview';
export * from './uncommon_processes';
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.ts
deleted file mode 100644
index 313275ce3c944..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/hosts/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-import type { HostsKpiHistogramData } from '../common';
-
-export interface HostsKpiHostsStrategyResponse extends IEsSearchResponse {
- hosts: Maybe;
- hostsHistogram: Maybe;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts
deleted file mode 100644
index fec98228d3ff6..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export * from '../../users/kpi/authentications';
-export * from './common';
-export * from './hosts';
-export * from './unique_ips';
-
-import type { UsersKpiAuthenticationsStrategyResponse } from '../../users/kpi/authentications';
-import type { HostsKpiHostsStrategyResponse } from './hosts';
-import type { HostsKpiUniqueIpsStrategyResponse } from './unique_ips';
-
-export enum HostsKpiQueries {
- kpiHosts = 'hostsKpiHosts',
- kpiUniqueIps = 'hostsKpiUniqueIps',
-}
-
-export type HostsKpiStrategyResponse =
- | Omit
- | Omit
- | Omit;
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.ts
deleted file mode 100644
index 22cbad1ffd44a..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/hosts/kpi/unique_ips/index.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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-import type { HostsKpiHistogramData } from '../common';
-
-export interface HostsKpiUniqueIpsStrategyResponse extends IEsSearchResponse {
- uniqueSourceIps: Maybe;
- uniqueSourceIpsHistogram: Maybe;
- uniqueDestinationIps: Maybe;
- uniqueDestinationIpsHistogram: Maybe;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts
index ee8006ca08b91..c4ec7d767616f 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/index.ts
@@ -11,9 +11,6 @@ import type {
HostsQueries,
HostsStrategyResponse,
HostsUncommonProcessesStrategyResponse,
- HostsKpiQueries,
- HostsKpiHostsStrategyResponse,
- HostsKpiUniqueIpsStrategyResponse,
} from './hosts';
import type {
NetworkQueries,
@@ -25,15 +22,8 @@ import type {
NetworkTopCountriesStrategyResponse,
NetworkTopNFlowStrategyResponse,
NetworkUsersStrategyResponse,
- NetworkKpiQueries,
- NetworkKpiDnsStrategyResponse,
- NetworkKpiNetworkEventsStrategyResponse,
- NetworkKpiTlsHandshakesStrategyResponse,
- NetworkKpiUniqueFlowsStrategyResponse,
- NetworkKpiUniquePrivateIpsStrategyResponse,
NetworkTopNFlowCountStrategyResponse,
} from './network';
-import type { MatrixHistogramQuery, MatrixHistogramStrategyResponse } from './matrix_histogram';
import type {
CtiEventEnrichmentStrategyResponse,
CtiQueries,
@@ -48,9 +38,6 @@ import type {
} from './risk_score';
import type { UsersQueries } from './users';
import type { ObservedUserDetailsStrategyResponse } from './users/observed_details';
-import type { TotalUsersKpiStrategyResponse } from './users/kpi/total_users';
-
-import type { UsersKpiAuthenticationsStrategyResponse } from './users/kpi/authentications';
import type { UsersStrategyResponse } from './users/all';
import type { UserAuthenticationsStrategyResponse } from './users/authentications';
@@ -61,8 +48,6 @@ import type { UsersRelatedHostsStrategyResponse } from './related_entities/relat
import type { HostsRelatedUsersStrategyResponse } from './related_entities/related_users';
import type {
- AuthenticationsKpiRequestOptions,
- AuthenticationsKpiRequestOptionsInput,
EventEnrichmentRequestOptions,
EventEnrichmentRequestOptionsInput,
FirstLastSeenRequestOptions,
@@ -75,30 +60,14 @@ import type {
HostsRequestOptionsInput,
HostUncommonProcessesRequestOptions,
HostUncommonProcessesRequestOptionsInput,
- KpiHostsRequestOptions,
- KpiHostsRequestOptionsInput,
- KpiUniqueIpsRequestOptions,
- KpiUniqueIpsRequestOptionsInput,
ManagedUserDetailsRequestOptions,
ManagedUserDetailsRequestOptionsInput,
- MatrixHistogramRequestOptions,
- MatrixHistogramRequestOptionsInput,
NetworkDetailsRequestOptions,
NetworkDetailsRequestOptionsInput,
NetworkDnsRequestOptions,
NetworkDnsRequestOptionsInput,
NetworkHttpRequestOptions,
NetworkHttpRequestOptionsInput,
- NetworkKpiDnsRequestOptions,
- NetworkKpiDnsRequestOptionsInput,
- NetworkKpiEventsRequestOptions,
- NetworkKpiEventsRequestOptionsInput,
- NetworkKpiTlsHandshakesRequestOptions,
- NetworkKpiTlsHandshakesRequestOptionsInput,
- NetworkKpiUniqueFlowsRequestOptions,
- NetworkKpiUniqueFlowsRequestOptionsInput,
- NetworkKpiUniquePrivateIpsRequestOptions,
- NetworkKpiUniquePrivateIpsRequestOptionsInput,
NetworkOverviewRequestOptions,
NetworkOverviewRequestOptionsInput,
NetworkTlsRequestOptions,
@@ -123,8 +92,6 @@ import type {
RiskScoreRequestOptionsInput,
ThreatIntelSourceRequestOptions,
ThreatIntelSourceRequestOptionsInput,
- TotalUsersKpiRequestOptions,
- TotalUsersKpiRequestOptionsInput,
UserAuthenticationsRequestOptions,
UserAuthenticationsRequestOptionsInput,
UsersRequestOptions,
@@ -134,7 +101,6 @@ import type {
export * from './cti';
export * from './hosts';
export * from './risk_score';
-export * from './matrix_histogram';
export * from './network';
export * from './users';
export * from './first_last_seen';
@@ -142,13 +108,10 @@ export * from './related_entities';
export type FactoryQueryTypes =
| HostsQueries
- | HostsKpiQueries
| UsersQueries
| NetworkQueries
- | NetworkKpiQueries
| RiskQueries
| CtiQueries
- | typeof MatrixHistogramQuery
| typeof FirstLastSeenQuery
| RelatedEntitiesQueries;
@@ -162,22 +125,14 @@ export type StrategyResponseType = T extends HostsQ
? FirstLastSeenStrategyResponse
: T extends HostsQueries.uncommonProcesses
? HostsUncommonProcessesStrategyResponse
- : T extends HostsKpiQueries.kpiHosts
- ? HostsKpiHostsStrategyResponse
- : T extends HostsKpiQueries.kpiUniqueIps
- ? HostsKpiUniqueIpsStrategyResponse
: T extends UsersQueries.observedDetails
? ObservedUserDetailsStrategyResponse
: T extends UsersQueries.managedDetails
? ManagedUserDetailsStrategyResponse
- : T extends UsersQueries.kpiTotalUsers
- ? TotalUsersKpiStrategyResponse
: T extends UsersQueries.authentications
? UserAuthenticationsStrategyResponse
: T extends UsersQueries.users
? UsersStrategyResponse
- : T extends UsersQueries.kpiAuthentications
- ? UsersKpiAuthenticationsStrategyResponse
: T extends NetworkQueries.details
? NetworkDetailsStrategyResponse
: T extends NetworkQueries.dns
@@ -196,18 +151,6 @@ export type StrategyResponseType = T extends HostsQ
? NetworkTopNFlowCountStrategyResponse
: T extends NetworkQueries.users
? NetworkUsersStrategyResponse
- : T extends NetworkKpiQueries.dns
- ? NetworkKpiDnsStrategyResponse
- : T extends NetworkKpiQueries.networkEvents
- ? NetworkKpiNetworkEventsStrategyResponse
- : T extends NetworkKpiQueries.tlsHandshakes
- ? NetworkKpiTlsHandshakesStrategyResponse
- : T extends NetworkKpiQueries.uniqueFlows
- ? NetworkKpiUniqueFlowsStrategyResponse
- : T extends NetworkKpiQueries.uniquePrivateIps
- ? NetworkKpiUniquePrivateIpsStrategyResponse
- : T extends typeof MatrixHistogramQuery
- ? MatrixHistogramStrategyResponse
: T extends CtiQueries.eventEnrichment
? CtiEventEnrichmentStrategyResponse
: T extends CtiQueries.dataSource
@@ -234,22 +177,14 @@ export type StrategyRequestInputType = T extends Ho
? FirstLastSeenRequestOptionsInput
: T extends HostsQueries.uncommonProcesses
? HostUncommonProcessesRequestOptionsInput
- : T extends HostsKpiQueries.kpiHosts
- ? KpiHostsRequestOptionsInput
- : T extends HostsKpiQueries.kpiUniqueIps
- ? KpiUniqueIpsRequestOptionsInput
: T extends UsersQueries.authentications
? UserAuthenticationsRequestOptionsInput
: T extends UsersQueries.observedDetails
? ObservedUserDetailsRequestOptionsInput
: T extends UsersQueries.managedDetails
? ManagedUserDetailsRequestOptionsInput
- : T extends UsersQueries.kpiTotalUsers
- ? TotalUsersKpiRequestOptionsInput
: T extends UsersQueries.users
? UsersRequestOptionsInput
- : T extends UsersQueries.kpiAuthentications
- ? AuthenticationsKpiRequestOptionsInput
: T extends NetworkQueries.details
? NetworkDetailsRequestOptionsInput
: T extends NetworkQueries.dns
@@ -268,18 +203,6 @@ export type StrategyRequestInputType = T extends Ho
? NetworkTopNFlowCountRequestOptionsInput
: T extends NetworkQueries.users
? NetworkUsersRequestOptionsInput
- : T extends NetworkKpiQueries.dns
- ? NetworkKpiDnsRequestOptionsInput
- : T extends NetworkKpiQueries.networkEvents
- ? NetworkKpiEventsRequestOptionsInput
- : T extends NetworkKpiQueries.tlsHandshakes
- ? NetworkKpiTlsHandshakesRequestOptionsInput
- : T extends NetworkKpiQueries.uniqueFlows
- ? NetworkKpiUniqueFlowsRequestOptionsInput
- : T extends NetworkKpiQueries.uniquePrivateIps
- ? NetworkKpiUniquePrivateIpsRequestOptionsInput
- : T extends typeof MatrixHistogramQuery
- ? MatrixHistogramRequestOptionsInput
: T extends CtiQueries.eventEnrichment
? EventEnrichmentRequestOptionsInput
: T extends CtiQueries.dataSource
@@ -306,22 +229,14 @@ export type StrategyRequestType = T extends HostsQu
? FirstLastSeenRequestOptions
: T extends HostsQueries.uncommonProcesses
? HostUncommonProcessesRequestOptions
- : T extends HostsKpiQueries.kpiHosts
- ? KpiHostsRequestOptions
- : T extends HostsKpiQueries.kpiUniqueIps
- ? KpiUniqueIpsRequestOptions
: T extends UsersQueries.authentications
? UserAuthenticationsRequestOptions
: T extends UsersQueries.observedDetails
? ObservedUserDetailsRequestOptions
: T extends UsersQueries.managedDetails
? ManagedUserDetailsRequestOptions
- : T extends UsersQueries.kpiTotalUsers
- ? TotalUsersKpiRequestOptions
: T extends UsersQueries.users
? UsersRequestOptions
- : T extends UsersQueries.kpiAuthentications
- ? AuthenticationsKpiRequestOptions
: T extends NetworkQueries.details
? NetworkDetailsRequestOptions
: T extends NetworkQueries.dns
@@ -340,18 +255,6 @@ export type StrategyRequestType = T extends HostsQu
? NetworkTopNFlowCountRequestOptions
: T extends NetworkQueries.users
? NetworkUsersRequestOptions
- : T extends NetworkKpiQueries.dns
- ? NetworkKpiDnsRequestOptions
- : T extends NetworkKpiQueries.networkEvents
- ? NetworkKpiEventsRequestOptions
- : T extends NetworkKpiQueries.tlsHandshakes
- ? NetworkKpiTlsHandshakesRequestOptions
- : T extends NetworkKpiQueries.uniqueFlows
- ? NetworkKpiUniqueFlowsRequestOptions
- : T extends NetworkKpiQueries.uniquePrivateIps
- ? NetworkKpiUniquePrivateIpsRequestOptions
- : T extends typeof MatrixHistogramQuery
- ? MatrixHistogramRequestOptions
: T extends CtiQueries.eventEnrichment
? EventEnrichmentRequestOptions
: T extends CtiQueries.dataSource
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/anomalies/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/anomalies/index.ts
deleted file mode 100644
index d2eb1282ac880..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/anomalies/index.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.
- */
-
-interface AnomaliesOverTimeHistogramData {
- key_as_string: string;
- key: number;
- doc_count: number;
-}
-
-export interface AnomaliesActionGroupData {
- key: number;
- anomalies: {
- bucket: AnomaliesOverTimeHistogramData[];
- };
- doc_count: number;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/authentications/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/authentications/index.ts
deleted file mode 100644
index 5c57f52b74276..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/authentications/index.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.
- */
-
-export interface AuthenticationsOverTimeHistogramData {
- key_as_string: string;
- key: number;
- doc_count: number;
-}
-
-export interface AuthenticationsActionGroupData {
- key: number;
- events: {
- bucket: AuthenticationsOverTimeHistogramData[];
- };
- doc_count: number;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/dns/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/dns/index.ts
deleted file mode 100644
index 0bff9e11487f9..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/dns/index.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.
- */
-
-export interface DnsHistogramSubBucket {
- key: string;
- doc_count: number;
- orderAgg: {
- value: number;
- };
-}
-interface DnsHistogramBucket {
- doc_count_error_upper_bound: number;
- sum_other_doc_count: number;
- buckets: DnsHistogramSubBucket[];
-}
-
-export interface DnsHistogramGroupData {
- key: number;
- doc_count: number;
- key_as_string: string;
- histogram: DnsHistogramBucket;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/events/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/events/index.ts
deleted file mode 100644
index ec2ad46a9b75e..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/events/index.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { SearchHit } from '../../../common';
-
-interface EventsMatrixHistogramData {
- key_as_string: string;
- key: number;
- doc_count: number;
-}
-
-export interface EventSource {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- [field: string]: any;
-}
-
-export interface EventsActionGroupData {
- key: number | string;
- events: {
- bucket: EventsMatrixHistogramData[];
- };
- doc_count: number;
-}
-
-export interface Fields {
- [x: string]: T | Array>;
-}
-
-export interface EventHit extends SearchHit {
- sort: string[];
- _source: EventSource;
- fields: Fields;
- aggregations: {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- [agg: string]: any;
- };
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts
deleted file mode 100644
index 7677b4b5b8824..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/index.ts
+++ /dev/null
@@ -1,87 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { MatrixHistogramRequestOptions } from '../../../api/search_strategy/matrix_histogram/matrix_histogram';
-import type { Inspect, Maybe } from '../../common';
-import type { AlertsGroupData } from './alerts';
-import type { AnomaliesActionGroupData } from './anomalies';
-import type { DnsHistogramGroupData } from './dns';
-import type { AuthenticationsActionGroupData } from './authentications';
-import type { EventsActionGroupData } from './events';
-import type { PreviewHistogramGroupData } from './preview';
-
-export * from './alerts';
-export * from './anomalies';
-export * from './authentications';
-export * from './common';
-export * from './dns';
-export * from './events';
-export * from './preview';
-
-export { MatrixHistogramQuery } from '../../../api/search_strategy';
-
-export enum MatrixHistogramType {
- authentications = 'authentications',
- anomalies = 'anomalies',
- events = 'events',
- alerts = 'alerts',
- dns = 'dns',
- preview = 'preview',
-}
-
-export const MatrixHistogramTypeToAggName = {
- [MatrixHistogramType.alerts]: 'aggregations.alertsGroup.buckets',
- [MatrixHistogramType.anomalies]: 'aggregations.anomalyActionGroup.buckets',
- [MatrixHistogramType.authentications]: 'aggregations.eventActionGroup.buckets',
- [MatrixHistogramType.dns]: 'aggregations.dns_name_query_count.buckets',
- [MatrixHistogramType.events]: 'aggregations.eventActionGroup.buckets',
- [MatrixHistogramType.preview]: 'aggregations.preview.buckets',
-};
-
-export interface MatrixHistogramStrategyResponse extends IEsSearchResponse {
- inspect?: Maybe;
- matrixHistogramData: MatrixHistogramData[];
- totalCount: number;
-}
-
-export interface MatrixHistogramData {
- x?: Maybe;
- y?: Maybe;
- g?: Maybe;
-}
-
-export interface MatrixHistogramBucket {
- key: number;
- doc_count: number;
-}
-
-export interface MatrixHistogramSchema {
- buildDsl: (options: MatrixHistogramRequestOptions) => {};
- aggName: string;
- parseKey: string;
- parser?: (data: MatrixHistogramParseData, keyBucket: string) => MatrixHistogramData[];
-}
-
-export type MatrixHistogramParseData = T extends MatrixHistogramType.alerts
- ? AlertsGroupData[]
- : T extends MatrixHistogramType.anomalies
- ? AnomaliesActionGroupData[]
- : T extends MatrixHistogramType.dns
- ? DnsHistogramGroupData[]
- : T extends MatrixHistogramType.authentications
- ? AuthenticationsActionGroupData[]
- : T extends MatrixHistogramType.events
- ? EventsActionGroupData[]
- : T extends MatrixHistogramType.preview
- ? PreviewHistogramGroupData[]
- : never;
-
-export type MatrixHistogramDataConfig = Record<
- MatrixHistogramType,
- MatrixHistogramSchema
->;
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/preview/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/preview/index.ts
deleted file mode 100644
index 1c58eca3b4d9b..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/preview/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { HistogramBucket } from '../common';
-
-export interface PreviewHistogramGroupData {
- key: string;
- doc_count: number;
- preview: {
- buckets: HistogramBucket[];
- };
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/index.ts
index 469bd0eaf38bd..919360fc1ae4f 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/index.ts
@@ -9,7 +9,6 @@ export * from './common';
export * from './details';
export * from './dns';
export * from './http';
-export * from './kpi';
export * from './overview';
export * from './tls';
export * from './top_countries';
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/dns/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/dns/index.ts
deleted file mode 100644
index 3f006530aab19..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/dns/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-
-export interface NetworkKpiDnsStrategyResponse extends IEsSearchResponse {
- dnsQueries: number;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/index.ts
deleted file mode 100644
index 7c482608cea04..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/index.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export * from './dns';
-export * from './network_events';
-export * from './tls_handshakes';
-export * from './unique_flows';
-export * from './unique_private_ips';
-
-import type { NetworkKpiDnsStrategyResponse } from './dns';
-import type { NetworkKpiNetworkEventsStrategyResponse } from './network_events';
-import type { NetworkKpiTlsHandshakesStrategyResponse } from './tls_handshakes';
-import type { NetworkKpiUniqueFlowsStrategyResponse } from './unique_flows';
-import type { NetworkKpiUniquePrivateIpsStrategyResponse } from './unique_private_ips';
-
-export enum NetworkKpiQueries {
- dns = 'networkKpiDns',
- networkEvents = 'networkKpiNetworkEvents',
- tlsHandshakes = 'networkKpiTlsHandshakes',
- uniqueFlows = 'networkKpiUniqueFlows',
- uniquePrivateIps = 'networkKpiUniquePrivateIps',
-}
-
-export type NetworkKpiStrategyResponse =
- | Omit
- | Omit
- | Omit
- | Omit
- | Omit;
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/network_events/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/network_events/index.ts
deleted file mode 100644
index 781fd8ddfd90a..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/network_events/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-
-export interface NetworkKpiNetworkEventsStrategyResponse extends IEsSearchResponse {
- networkEvents: number;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/tls_handshakes/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/tls_handshakes/index.ts
deleted file mode 100644
index c2219c6ec8233..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/tls_handshakes/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-
-export interface NetworkKpiTlsHandshakesStrategyResponse extends IEsSearchResponse {
- tlsHandshakes: number;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_flows/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_flows/index.ts
deleted file mode 100644
index eba5f19ddc246..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_flows/index.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-
-export interface NetworkKpiUniqueFlowsStrategyResponse extends IEsSearchResponse {
- uniqueFlowId: number;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_private_ips/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_private_ips/index.ts
deleted file mode 100644
index d002452702332..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/network/kpi/unique_private_ips/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, Maybe } from '../../../../common';
-
-export interface NetworkKpiHistogramData {
- x?: Maybe;
- y?: Maybe;
-}
-
-export interface NetworkKpiUniquePrivateIpsStrategyResponse extends IEsSearchResponse {
- uniqueSourcePrivateIps: number;
- uniqueSourcePrivateIpsHistogram: NetworkKpiHistogramData[] | null;
- uniqueDestinationPrivateIps: number;
- uniqueDestinationPrivateIpsHistogram: NetworkKpiHistogramData[] | null;
- inspect?: Maybe;
-}
-
-export type UniquePrivateAttributeQuery = 'source' | 'destination';
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/index.ts
index 9989f2eb6d331..27ef092c15019 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/index.ts
@@ -5,14 +5,9 @@
* 2.0.
*/
-import type { TotalUsersKpiStrategyResponse } from './kpi/total_users';
-
export * from './all';
export * from './common';
-export * from './kpi';
export * from './observed_details';
export * from './authentications';
export { UsersQueries } from '../../../api/search_strategy';
-
-export type UsersKpiStrategyResponse = Omit;
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/authentications/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/authentications/index.ts
deleted file mode 100644
index cd6fd18b86c87..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/authentications/index.ts
+++ /dev/null
@@ -1,17 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, KpiHistogramData, Maybe } from '../../../../common';
-
-export interface UsersKpiAuthenticationsStrategyResponse extends IEsSearchResponse {
- authenticationsSuccess: Maybe;
- authenticationsSuccessHistogram: Maybe;
- authenticationsFailure: Maybe;
- authenticationsFailureHistogram: Maybe;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/total_users/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/total_users/index.ts
deleted file mode 100644
index 690678c1d78d9..0000000000000
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/kpi/total_users/index.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 type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { Inspect, KpiHistogramData, Maybe } from '../../../../common';
-
-export interface TotalUsersKpiStrategyResponse extends IEsSearchResponse {
- users: Maybe;
- usersHistogram: Maybe;
- inspect?: Maybe;
-}
diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx
index cd2810a5689fb..b6e8985c1d15d 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx
@@ -175,7 +175,6 @@ const EventsQueryTabBodyComponent: React.FC =
startDate={startDate}
endDate={endDate}
filterQuery={filterQuery}
- indexNames={indexNames}
setQuery={setQuery}
{...(showExternalAlerts ? alertsHistogramConfig : eventsHistogramConfig)}
subtitle={getHistogramSubtitle}
diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/histogram_configurations.ts b/x-pack/plugins/security_solution/public/common/components/events_tab/histogram_configurations.ts
index f341f090710a0..12458d5c52a5f 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_tab/histogram_configurations.ts
+++ b/x-pack/plugins/security_solution/public/common/components/events_tab/histogram_configurations.ts
@@ -6,7 +6,6 @@
*/
import numeral from '@elastic/numeral';
-import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
import { getExternalAlertLensAttributes } from '../visualization_actions/lens_attributes/common/external_alert';
import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_attributes/common/events';
import type { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
@@ -38,8 +37,6 @@ export const eventsStackByOptions: MatrixHistogramOption[] = [
export const eventsHistogramConfig: MatrixHistogramConfigs = {
defaultStackByOption:
eventsStackByOptions.find((o) => o.text === DEFAULT_EVENTS_STACK_BY) ?? eventsStackByOptions[0],
- errorMessage: i18n.ERROR_FETCHING_EVENTS_DATA,
- histogramType: MatrixHistogramType.events,
stackByOptions: eventsStackByOptions,
subtitle: undefined,
title: i18n.EVENTS_GRAPH_TITLE,
@@ -62,10 +59,7 @@ const DEFAULT_STACK_BY = 'event.module';
export const alertsHistogramConfig: MatrixHistogramConfigs = {
defaultStackByOption:
alertsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0],
- errorMessage: i18n.ERROR_FETCHING_ALERTS_DATA,
- histogramType: MatrixHistogramType.alerts,
stackByOptions: alertsStackByOptions,
- subtitle: undefined,
title: i18n.ALERTS_GRAPH_TITLE,
getLensAttributes: getExternalAlertLensAttributes,
};
diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx
index cd8e83ea6f0f0..de37ea047335e 100644
--- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/onboarding.test.tsx
@@ -22,6 +22,7 @@ jest.mock('../../../lib/kibana', () => {
const original = jest.requireActual('../../../lib/kibana');
return {
...original,
+ useCurrentUser: jest.fn().mockReturnValue({ fullName: 'UserFullName' }),
useAppUrl: jest.fn().mockReturnValue({ getAppUrl: jest.fn().mockReturnValue('mock url') }),
};
});
@@ -63,10 +64,14 @@ describe('OnboardingComponent', () => {
ViewAlertsSteps.viewAlerts,
],
};
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
it('should render page title, subtitle, and description', () => {
const { getByText } = render( );
- const pageTitle = getByText('Hi Unknown!');
+ const pageTitle = getByText('Hi UserFullName!');
const subtitle = getByText(`Get started with Security`);
const description = getByText(
`This area shows you everything you need to know. Feel free to explore all content. You can always come back here at any time.`
diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.test.tsx
new file mode 100644
index 0000000000000..0eaf44e9cc69c
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.test.tsx
@@ -0,0 +1,86 @@
+/*
+ * 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 { WelcomeHeader } from '.';
+import { useCurrentUser } from '../../../../lib/kibana';
+import { CurrentPlan } from './current_plan';
+import { ProductTier } from '../configs';
+import { useProjectFeaturesUrl } from '../hooks/use_project_features_url';
+
+jest.mock('../../../../lib/kibana', () => ({
+ useCurrentUser: jest.fn(),
+}));
+
+jest.mock('../hooks/use_project_features_url', () => ({
+ useProjectFeaturesUrl: jest.fn(),
+}));
+
+jest.mock('./current_plan', () => ({
+ CurrentPlan: jest.fn().mockReturnValue(
),
+}));
+
+const mockUseCurrentUser = useCurrentUser as jest.Mock;
+const mockCurrentPlan = CurrentPlan as unknown as jest.Mock;
+const mockUseProjectFeaturesUrl = useProjectFeaturesUrl as jest.Mock;
+const mockProjectFeaturesUrl = '/features';
+
+describe('WelcomeHeaderComponent', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render fullName when fullName is provided', () => {
+ const fullName = 'John Doe';
+ mockUseCurrentUser.mockReturnValue({ fullName });
+ const { getByText } = render( );
+ const titleElement = getByText(`Hi ${fullName}!`);
+ expect(titleElement).toBeInTheDocument();
+ });
+
+ it('should render username when fullName is not provided', () => {
+ const username = 'jd';
+ mockUseCurrentUser.mockReturnValue({ username });
+
+ const { getByText } = render( );
+ const titleElement = getByText(`Hi ${username}!`);
+ expect(titleElement).toBeInTheDocument();
+ });
+
+ it('should not render the greeting message if both fullName and username are not available', () => {
+ mockUseCurrentUser.mockReturnValue({});
+
+ const { queryByTestId } = render( );
+ const greetings = queryByTestId(`welcome-header-greetings`);
+ expect(greetings).not.toBeInTheDocument();
+ });
+
+ it('should render subtitle', () => {
+ const { getByText } = render( );
+ const subtitleElement = getByText('Get started with Security');
+ expect(subtitleElement).toBeInTheDocument();
+ });
+
+ it('should render description', () => {
+ const { getByText } = render( );
+ const descriptionElement = getByText(
+ 'This area shows you everything you need to know. Feel free to explore all content. You can always come back here at any time.'
+ );
+ expect(descriptionElement).toBeInTheDocument();
+ });
+
+ it('should render current plan', () => {
+ mockUseProjectFeaturesUrl.mockReturnValue(mockProjectFeaturesUrl);
+ const { getByTestId } = render( );
+ const currentPlanElement = getByTestId('current-plan');
+ expect(currentPlanElement).toBeInTheDocument();
+ expect(mockCurrentPlan.mock.calls[0][0]).toEqual({
+ productTier: 'complete',
+ projectFeaturesUrl: mockProjectFeaturesUrl,
+ });
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx
index b03829c29c8cc..c4d5fa13ec6f6 100644
--- a/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/landing_page/onboarding/welcome_header/index.tsx
@@ -22,6 +22,10 @@ import { CurrentPlan } from './current_plan';
const WelcomeHeaderComponent: React.FC<{ productTier?: ProductTier }> = ({ productTier }) => {
const userName = useCurrentUser();
+
+ // Full name could be null, user name should always exist
+ const name = userName?.fullName ?? userName?.username;
+
const projectFeaturesUrl = useProjectFeaturesUrl();
const {
@@ -38,9 +42,13 @@ const WelcomeHeaderComponent: React.FC<{ productTier?: ProductTier }> = ({ produ
return (
- {userName?.username && (
-
- {GET_STARTED_PAGE_TITLE(userName.username)}
+ {name && (
+
+ {GET_STARTED_PAGE_TITLE(name)}
)}
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/chart_content.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/chart_content.tsx
deleted file mode 100644
index 65d3774af1eef..0000000000000
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/chart_content.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import type { BarChartComponentProps } from '../charts/barchart';
-import { BarChart } from '../charts/barchart';
-import { MatrixLoader } from './matrix_loader';
-
-const MatrixHistogramChartContentComponent = ({
- isInitialLoading,
- barChart,
- configs,
- stackByField,
- scopeId,
-}: BarChartComponentProps & { isInitialLoading: boolean }) => {
- return isInitialLoading ? (
-
- ) : (
-
- );
-};
-
-export const MatrixHistogramChartContent = React.memo(MatrixHistogramChartContentComponent);
-
-MatrixHistogramChartContentComponent.displayName = 'MatrixHistogramChartContentComponent';
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx
index ac254714adbb7..c9c2b5cb25c62 100644
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx
@@ -10,29 +10,13 @@ import { mount } from 'enzyme';
import React from 'react';
import { MatrixHistogram } from '.';
-import { useMatrixHistogramCombined } from '../../containers/matrix_histogram';
-import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
import { TestProviders } from '../../mock';
import { mockRuntimeMappings } from '../../containers/source/mock';
import { getDnsTopDomainsLensAttributes } from '../visualization_actions/lens_attributes/network/dns_top_domains';
import { useQueryToggle } from '../../containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
-import type { ExperimentalFeatures } from '../../../../common/experimental_features';
-import { allowedExperimentalValues } from '../../../../common/experimental_features';
-import { VisualizationActions } from '../visualization_actions/actions';
jest.mock('../../containers/query_toggle');
-jest.mock('./matrix_loader', () => ({
- MatrixLoader: () =>
,
-}));
-
-jest.mock('../charts/barchart', () => ({
- BarChart: () =>
,
-}));
-
-jest.mock('../../containers/matrix_histogram');
-
jest.mock('../visualization_actions/actions');
jest.mock('../visualization_actions/visualization_embeddable');
@@ -40,20 +24,16 @@ jest.mock('../../hooks/use_experimental_features', () => ({
useIsExperimentalFeatureEnabled: jest.fn(),
}));
-jest.mock('./utils', () => ({
- getBarchartConfigs: jest.fn(),
- getCustomChartData: jest.fn().mockReturnValue(true),
-}));
-
const mockUseVisualizationResponse = jest.fn(() => ({
responses: [{ aggregations: [{ buckets: [{ key: '1234' }] }], hits: { total: 999 } }],
+ requests: [],
+ loading: false,
}));
jest.mock('../visualization_actions/use_visualization_response', () => ({
useVisualizationResponse: () => mockUseVisualizationResponse(),
}));
const mockLocation = jest.fn().mockReturnValue({ pathname: '/test' });
-const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock;
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
@@ -74,15 +54,11 @@ describe('Matrix Histogram Component', () => {
value: 'dns.question.registered_domain',
},
endDate: '2019-07-18T20:00:00.000Z',
- errorMessage: 'error',
- histogramType: MatrixHistogramType.alerts,
id: 'mockId',
indexNames: [],
isInspected: false,
isPtrIncluded: true,
setQuery: jest.fn(),
- skip: false,
- sourceId: 'default',
stackByOptions: [
{ text: 'dns.question.registered_domain', value: 'dns.question.registered_domain' },
],
@@ -92,51 +68,51 @@ describe('Matrix Histogram Component', () => {
title: 'mockTitle',
runtimeMappings: mockRuntimeMappings,
};
- const mockUseMatrix = useMatrixHistogramCombined as jest.Mock;
const mockUseQueryToggle = useQueryToggle as jest.Mock;
const mockSetToggle = jest.fn();
- const getMockUseIsExperimentalFeatureEnabled =
- (mockMapping?: Partial) =>
- (flag: keyof typeof allowedExperimentalValues) =>
- mockMapping ? mockMapping?.[flag] : allowedExperimentalValues?.[flag];
beforeEach(() => {
jest.clearAllMocks();
- mockUseIsExperimentalFeatureEnabled.mockImplementation(
- getMockUseIsExperimentalFeatureEnabled({ chartEmbeddablesEnabled: false })
- );
+
mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle });
- mockUseMatrix.mockReturnValue([
- false,
- {
- data: null,
- inspect: false,
- totalCount: null,
- },
- ]);
});
- describe('on initial load', () => {
+ describe('rendering', () => {
beforeEach(() => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
});
- test('it requests Matrix Histogram', () => {
- expect(mockUseMatrix).toHaveBeenCalledWith({
- endDate: mockMatrixOverTimeHistogramProps.endDate,
- errorMessage: mockMatrixOverTimeHistogramProps.errorMessage,
- histogramType: mockMatrixOverTimeHistogramProps.histogramType,
- indexNames: mockMatrixOverTimeHistogramProps.indexNames,
- startDate: mockMatrixOverTimeHistogramProps.startDate,
- stackByField: mockMatrixOverTimeHistogramProps.defaultStackByOption.value,
- runtimeMappings: mockMatrixOverTimeHistogramProps.runtimeMappings,
- isPtrIncluded: mockMatrixOverTimeHistogramProps.isPtrIncluded,
- skip: mockMatrixOverTimeHistogramProps.skip,
- });
+
+ test('it should not render VisualizationActions', () => {
+ expect(wrapper.find(`[data-test-subj="visualizationActions"]`).exists()).toEqual(false);
+ });
+
+ test('it should render Lens Visualization', () => {
+ expect(wrapper.find(`[data-test-subj="visualization-embeddable"]`).exists()).toEqual(true);
+ });
+
+ test('it should render visualization count as subtitle', () => {
+ wrapper.setProps({ endDate: 100 });
+ wrapper.update();
+
+ expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(
+ 'Showing: 999 events'
+ );
});
- test('it renders MatrixLoader', () => {
- expect(wrapper.find('MatrixLoader').exists()).toBe(true);
+
+ test('it should render 0 as subtitle when buckets are empty', () => {
+ mockUseVisualizationResponse.mockReturnValue({
+ requests: [],
+ responses: [{ aggregations: [{ buckets: [] }], hits: { total: 999 } }],
+ loading: false,
+ });
+ wrapper.setProps({ endDate: 100 });
+ wrapper.update();
+
+ expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(
+ 'Showing: 0 events'
+ );
});
});
@@ -145,7 +121,7 @@ describe('Matrix Histogram Component', () => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
- expect(wrapper.find('[data-test-subj="spacer"]').exists()).toBe(true);
+ expect(wrapper.find('[data-test-subj="spacer"]').exists()).toEqual(true);
});
test('it does NOT render a spacer when showSpacer is false', () => {
@@ -155,39 +131,7 @@ describe('Matrix Histogram Component', () => {
wrappingComponent: TestProviders,
}
);
- expect(wrapper.find('[data-test-subj="spacer"]').exists()).toBe(false);
- });
- });
-
- describe('not initial load', () => {
- beforeEach(() => {
- wrapper = mount( , {
- wrappingComponent: TestProviders,
- });
- mockUseMatrix.mockReturnValue([
- false,
- {
- data: [
- { x: 1, y: 2, g: 'g1' },
- { x: 2, y: 4, g: 'g1' },
- { x: 3, y: 6, g: 'g1' },
- { x: 1, y: 1, g: 'g2' },
- { x: 2, y: 3, g: 'g2' },
- { x: 3, y: 5, g: 'g2' },
- ],
- inspect: false,
- totalCount: 1,
- },
- ]);
- wrapper.setProps({ endDate: 100 });
- wrapper.update();
- });
- test('it renders no MatrixLoader', () => {
- expect(wrapper.find(`MatrixLoader`).exists()).toBe(false);
- });
-
- test('it shows BarChart if data available', () => {
- expect(wrapper.find(`.barchart`).exists()).toBe(true);
+ expect(wrapper.find('[data-test-subj="spacer"]').exists()).toEqual(false);
});
});
@@ -196,12 +140,12 @@ describe('Matrix Histogram Component', () => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
- expect(wrapper.find('EuiSelect').exists()).toBe(false);
+ expect(wrapper.find('EuiSelect').exists()).toEqual(false);
});
});
describe('Inspect button', () => {
- test("it doesn't render Inspect button by default", () => {
+ test('it does not render Inspect button', () => {
const testProps = {
...mockMatrixOverTimeHistogramProps,
getLensAttributes: getDnsTopDomainsLensAttributes,
@@ -209,46 +153,7 @@ describe('Matrix Histogram Component', () => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
- expect(wrapper.find('[data-test-subj="inspect-icon-button"]').exists()).toBe(false);
- });
- });
-
- describe('VisualizationActions', () => {
- const testProps = {
- ...mockMatrixOverTimeHistogramProps,
- getLensAttributes: jest.fn().mockReturnValue(getDnsTopDomainsLensAttributes()),
- };
- beforeEach(() => {
- wrapper = mount( , {
- wrappingComponent: TestProviders,
- });
- });
- test('it renders VisualizationActions if getLensAttributes is provided', () => {
- expect(wrapper.find('[data-test-subj="visualizationActions"]').exists()).toBe(true);
- expect(wrapper.find('[data-test-subj="visualizationActions"]').prop('className')).toEqual(
- 'histogram-viz-actions'
- );
- });
-
- test('it VisualizationActions with correct properties', () => {
- expect((VisualizationActions as unknown as jest.Mock).mock.calls[0][0]).toEqual(
- expect.objectContaining({
- className: 'histogram-viz-actions',
- extraOptions: {
- dnsIsPtrIncluded: testProps.isPtrIncluded,
- },
- getLensAttributes: testProps.getLensAttributes,
- lensAttributes: undefined,
- isInspectButtonDisabled: true,
- queryId: testProps.id,
- stackByField: testProps.defaultStackByOption.value,
- timerange: {
- from: testProps.startDate,
- to: testProps.endDate,
- },
- title: testProps.title,
- })
- );
+ expect(wrapper.find('[data-test-subj="inspect-icon-button"]').exists()).toEqual(false);
});
});
@@ -262,25 +167,16 @@ describe('Matrix Histogram Component', () => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
- expect(mockUseMatrix.mock.calls[0][0].skip).toEqual(false);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(true);
wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click');
expect(mockSetToggle).toBeCalledWith(false);
- expect(mockUseMatrix.mock.calls[1][0].skip).toEqual(true);
- });
-
- test('toggleStatus=true, do not skip', () => {
- wrapper = mount( , {
- wrappingComponent: TestProviders,
- });
-
- expect(mockUseMatrix.mock.calls[0][0].skip).toEqual(false);
});
test('toggleStatus=true, render components', () => {
wrapper = mount( , {
wrappingComponent: TestProviders,
});
- expect(wrapper.find('MatrixLoader').exists()).toBe(true);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(true);
});
test('toggleStatus=false, do not render components', () => {
@@ -297,71 +193,7 @@ describe('Matrix Histogram Component', () => {
wrappingComponent: TestProviders,
});
- expect(mockUseMatrix.mock.calls[0][0].skip).toEqual(true);
- });
- });
-
- describe('when the chartEmbeddablesEnabled experimental feature flag is enabled', () => {
- beforeEach(() => {
- const mockMapping: Partial = {
- chartEmbeddablesEnabled: true,
- };
-
- mockUseIsExperimentalFeatureEnabled.mockImplementation(
- getMockUseIsExperimentalFeatureEnabled(mockMapping)
- );
-
- wrapper = mount( , {
- wrappingComponent: TestProviders,
- });
- });
- test('it should not render VisualizationActions', () => {
- expect(wrapper.find(`[data-test-subj="visualizationActions"]`).exists()).toEqual(false);
- });
-
- test('it should not fetch Matrix Histogram data', () => {
- expect(mockUseMatrix.mock.calls[0][0].skip).toEqual(true);
- });
-
- test('it should render Lens Embeddable', () => {
- expect(wrapper.find(`[data-test-subj="visualization-embeddable"]`).exists()).toEqual(true);
- });
-
- test('it should render visualization count as subtitle', () => {
- mockUseMatrix.mockReturnValue([
- false,
- {
- data: [],
- inspect: false,
- totalCount: 0,
- },
- ]);
- wrapper.setProps({ endDate: 100 });
- wrapper.update();
-
- expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(
- 'Showing: 999 events'
- );
- });
-
- test('it should render 0 as subtitle when buckets are empty', () => {
- mockUseVisualizationResponse.mockReturnValue({
- responses: [{ aggregations: [{ buckets: [] }], hits: { total: 999 } }],
- });
- mockUseMatrix.mockReturnValue([
- false,
- {
- data: [],
- inspect: false,
- totalCount: 0,
- },
- ]);
- wrapper.setProps({ endDate: 100 });
- wrapper.update();
-
- expect(wrapper.find(`[data-test-subj="header-section-subtitle"]`).text()).toEqual(
- 'Showing: 0 events'
- );
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(false);
});
});
});
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx
index 28615317c5d84..0009108596847 100644
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.tsx
@@ -6,68 +6,37 @@
*/
import React, { useState, useEffect, useCallback, useMemo } from 'react';
-import type { Position } from '@elastic/charts';
import styled from 'styled-components';
-import { EuiFlexGroup, EuiFlexItem, EuiProgress, EuiSelect, EuiSpacer } from '@elastic/eui';
-import { useDispatch } from 'react-redux';
+import { EuiFlexGroup, EuiFlexItem, EuiSelect, EuiSpacer } from '@elastic/eui';
import type { AggregationsTermsAggregateBase } from '@elastic/elasticsearch/lib/api/types';
import { isString } from 'lodash/fp';
import * as i18n from './translations';
import { HeaderSection } from '../header_section';
import { Panel } from '../panel';
-import { getBarchartConfigs, getCustomChartData } from './utils';
-import { useMatrixHistogramCombined } from '../../containers/matrix_histogram';
+
import type {
- MatrixHistogramProps,
MatrixHistogramOption,
MatrixHistogramQueryProps,
- MatrixHistogramMappingTypes,
- GetTitle,
- GetSubTitle,
+ MatrixHistogramConfigs,
} from './types';
-import type { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
import type { GlobalTimeArgs } from '../../containers/use_global_time';
-import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions';
-import { InputsModelId } from '../../store/inputs/constants';
import { HoverVisibilityContainer } from '../hover_visibility_container';
-import { VisualizationActions } from '../visualization_actions/actions';
-import type {
- GetLensAttributes,
- LensAttributes,
- VisualizationResponse,
-} from '../visualization_actions/types';
+import type { VisualizationResponse } from '../visualization_actions/types';
import { useQueryToggle } from '../../containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../visualization_actions/utils';
import { VisualizationEmbeddable } from '../visualization_actions/visualization_embeddable';
-import { MatrixHistogramChartContent } from './chart_content';
import { useVisualizationResponse } from '../visualization_actions/use_visualization_response';
import type { SourcererScopeName } from '../../store/sourcerer/model';
-export type MatrixHistogramComponentProps = MatrixHistogramProps &
- Omit & {
- defaultStackByOption: MatrixHistogramOption;
- errorMessage: string;
- getLensAttributes?: GetLensAttributes;
+export type MatrixHistogramComponentProps = MatrixHistogramQueryProps &
+ MatrixHistogramConfigs & {
headerChildren?: React.ReactNode;
hideHistogramIfEmpty?: boolean;
- histogramType: MatrixHistogramType;
id: string;
- legendPosition?: Position;
- lensAttributes?: LensAttributes;
- mapping?: MatrixHistogramMappingTypes;
- onError?: () => void;
showSpacer?: boolean;
setQuery: GlobalTimeArgs['setQuery'];
- showInspectButton?: boolean;
- setAbsoluteRangeDatePickerTarget?: InputsModelId;
- showLegend?: boolean;
- stackByOptions: MatrixHistogramOption[];
- subtitle?: string | GetSubTitle;
- scopeId?: string;
sourcererScopeId?: SourcererScopeName;
- title: string | GetTitle;
hideQueryToggle?: boolean;
applyGlobalQueriesAndFilters?: boolean;
};
@@ -95,71 +64,28 @@ export const MatrixHistogramComponent: React.FC =
chartHeight,
defaultStackByOption,
endDate,
- errorMessage,
filterQuery,
getLensAttributes,
headerChildren,
- histogramType,
hideHistogramIfEmpty = false,
id,
- indexNames,
- runtimeMappings,
isPtrIncluded,
- legendPosition,
lensAttributes,
- mapping,
- onError,
paddingSize = 'm',
panelHeight = DEFAULT_PANEL_HEIGHT,
- setAbsoluteRangeDatePickerTarget = InputsModelId.global,
setQuery,
- showInspectButton = false,
- showLegend,
showSpacer = true,
stackByOptions,
startDate,
subtitle,
- scopeId,
sourcererScopeId,
title,
titleSize,
- yTickFormatter,
- skip,
hideQueryToggle = false,
applyGlobalQueriesAndFilters = true,
}) => {
const visualizationId = `${id}-embeddable`;
- const dispatch = useDispatch();
- const handleBrushEnd = useCallback(
- ({ x }) => {
- if (!x) {
- return;
- }
- const [min, max] = x;
- dispatch(
- setAbsoluteRangeDatePicker({
- id: setAbsoluteRangeDatePickerTarget,
- from: new Date(min).toISOString(),
- to: new Date(max).toISOString(),
- })
- );
- },
- [dispatch, setAbsoluteRangeDatePickerTarget]
- );
- const barchartConfigs = useMemo(
- () =>
- getBarchartConfigs({
- chartHeight,
- from: startDate,
- legendPosition,
- to: endDate,
- onBrushEnd: handleBrushEnd,
- yTickFormatter,
- showLegend,
- }),
- [chartHeight, startDate, legendPosition, endDate, handleBrushEnd, yTickFormatter, showLegend]
- );
const [isInitialLoading, setIsInitialLoading] = useState(true);
const [selectedStackByOption, setSelectedStackByOption] =
useState(defaultStackByOption);
@@ -178,93 +104,48 @@ export const MatrixHistogramComponent: React.FC =
);
const { toggleStatus, setToggleStatus } = useQueryToggle(id);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
+
const toggleQuery = useCallback(
(status: boolean) => {
setToggleStatus(status);
- // toggle on = skipQuery false
- setQuerySkip(!status);
},
- [setQuerySkip, setToggleStatus]
+ [setToggleStatus]
);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- const matrixHistogramRequest = {
- endDate,
- errorMessage,
- filterQuery,
- histogramType,
- indexNames,
- onError,
- startDate,
- stackByField: selectedStackByOption.value,
- runtimeMappings,
- isPtrIncluded,
- skip: querySkip || isChartEmbeddablesEnabled,
- };
- const [loading, { data, inspect, totalCount, refetch }] =
- useMatrixHistogramCombined(matrixHistogramRequest);
-
const titleWithStackByField = useMemo(
() => (title != null && typeof title === 'function' ? title(selectedStackByOption) : title),
[title, selectedStackByOption]
);
- const { responses } = useVisualizationResponse({ visualizationId });
+ const { responses: visualizationResponses } = useVisualizationResponse({ visualizationId });
+ const visualizationTotalCount: number | null = useMemo(() => {
+ if (!visualizationResponses || !visualizationResponseHasData(visualizationResponses)) {
+ return 0;
+ }
+ return visualizationResponses[0].hits.total;
+ }, [visualizationResponses]);
+
const subtitleWithCounts = useMemo(() => {
if (isInitialLoading) {
return null;
}
if (typeof subtitle === 'function') {
- if (isChartEmbeddablesEnabled) {
- if (!responses || !visualizationResponseHasData(responses)) {
- return subtitle(0);
- }
- const visualizationCount = responses[0].hits.total;
- return visualizationCount >= 0 ? subtitle(visualizationCount) : null;
- } else {
- return totalCount >= 0 ? subtitle(totalCount) : null;
- }
+ return visualizationTotalCount >= 0 ? subtitle(visualizationTotalCount) : null;
}
return subtitle;
- }, [isChartEmbeddablesEnabled, isInitialLoading, responses, subtitle, totalCount]);
+ }, [isInitialLoading, subtitle, visualizationTotalCount]);
const hideHistogram = useMemo(
- () => (totalCount <= 0 && hideHistogramIfEmpty ? true : false),
- [totalCount, hideHistogramIfEmpty]
+ () => ((visualizationTotalCount ?? 0) <= 0 && hideHistogramIfEmpty ? true : false),
+ [hideHistogramIfEmpty, visualizationTotalCount]
);
- const barChartData = useMemo(() => getCustomChartData(data, mapping), [data, mapping]);
useEffect(() => {
- if (!loading && !isInitialLoading) {
- setQuery({
- id,
- inspect,
- loading,
- refetch,
- });
- }
-
- if (isInitialLoading && !!barChartData && data) {
+ if (isInitialLoading && !!visualizationResponses) {
setIsInitialLoading(false);
}
- }, [
- barChartData,
- data,
- id,
- inspect,
- isChartEmbeddablesEnabled,
- isInitialLoading,
- loading,
- refetch,
- setIsInitialLoading,
- setQuery,
- ]);
+ }, [id, isInitialLoading, visualizationResponses, setIsInitialLoading, setQuery]);
const timerange = useMemo(() => ({ from: startDate, to: endDate }), [startDate, endDate]);
const extraVisualizationOptions = useMemo(
@@ -297,15 +178,6 @@ export const MatrixHistogramComponent: React.FC =
height={toggleStatus ? panelHeight : undefined}
paddingSize={paddingSize}
>
- {loading && !isInitialLoading && (
-
- )}
-
=
toggleStatus={toggleStatus}
toggleQuery={hideQueryToggle ? undefined : toggleQuery}
subtitle={subtitleWithCounts}
- inspectMultiple
- showInspectButton={showInspectButton && !isChartEmbeddablesEnabled}
- isInspectDisabled={filterQuery === undefined}
+ showInspectButton={false}
>
- {(getLensAttributes || lensAttributes) && timerange && !isChartEmbeddablesEnabled && (
-
-
-
- )}
{stackByOptions.length > 1 && (
=
{toggleStatus ? (
- isChartEmbeddablesEnabled ? (
-
- ) : (
-
- )
+
) : null}
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts
index 491a3f3b37ca9..032a768df355e 100644
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts
+++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/types.ts
@@ -5,23 +5,11 @@
* 2.0.
*/
-import type React from 'react';
-import type { EuiTitleSize } from '@elastic/eui';
-import type { ScaleType, Position, TickFormatter } from '@elastic/charts';
-import type { ActionCreator } from 'redux';
-import type { RunTimeMappings } from '@kbn/timelines-plugin/common/api/search_strategy';
+import type { EuiPaddingSize, EuiTitleSize } from '@elastic/eui';
+import type { Position } from '@elastic/charts';
import type { ESQuery } from '../../../../common/typed_json';
-import type { InputsModelId } from '../../store/inputs/constants';
-import type { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
-import type { UpdateDateRange } from '../charts/common';
-import type { GlobalTimeArgs } from '../../containers/use_global_time';
-import type { FieldValueThreshold } from '../../../detection_engine/rule_creation_ui/components/threshold_input';
import type { GetLensAttributes, LensAttributes } from '../visualization_actions/types';
-export type MatrixHistogramMappingTypes = Record<
- string,
- { key: string; value: null; color?: string | undefined }
->;
export interface MatrixHistogramOption {
text: string;
value: string;
@@ -31,103 +19,23 @@ export type GetSubTitle = (count: number) => string;
export type GetTitle = (matrixHistogramOption: MatrixHistogramOption) => string;
export interface MatrixHistogramConfigs {
+ chartHeight?: number;
defaultStackByOption: MatrixHistogramOption;
- errorMessage: string;
getLensAttributes?: GetLensAttributes;
hideHistogramIfEmpty?: boolean;
- histogramType: MatrixHistogramType;
legendPosition?: Position;
lensAttributes?: LensAttributes;
- mapping?: MatrixHistogramMappingTypes;
- stackByOptions: MatrixHistogramOption[];
- subtitle?: string | GetSubTitle;
- title: string | GetTitle;
- titleSize?: EuiTitleSize;
-}
-
-interface MatrixHistogramBasicProps {
- chartHeight?: number;
- defaultStackByOption: MatrixHistogramOption;
- endDate: GlobalTimeArgs['to'];
- headerChildren?: React.ReactNode;
- hideHistogramIfEmpty?: boolean;
- id: string;
- legendPosition?: Position;
- mapping?: MatrixHistogramMappingTypes;
+ paddingSize?: EuiPaddingSize;
panelHeight?: number;
- paddingSize?: 's' | 'm' | 'l' | 'none';
- setQuery: GlobalTimeArgs['setQuery'];
- startDate: GlobalTimeArgs['from'];
stackByOptions: MatrixHistogramOption[];
subtitle?: string | GetSubTitle;
- title?: string | GetTitle;
+ title: string | GetTitle;
titleSize?: EuiTitleSize;
}
export interface MatrixHistogramQueryProps {
endDate: string;
- errorMessage: string;
- indexNames: string[];
filterQuery?: ESQuery | string | undefined;
- onError?: () => void;
- setAbsoluteRangeDatePicker?: ActionCreator<{
- id: InputsModelId;
- from: string;
- to: string;
- }>;
- setAbsoluteRangeDatePickerTarget?: InputsModelId;
- stackByField: string;
startDate: string;
- histogramType: MatrixHistogramType;
- threshold?: FieldValueThreshold;
- skip?: boolean;
isPtrIncluded?: boolean;
- includeMissingData?: boolean;
- runtimeMappings?: RunTimeMappings;
-}
-
-export interface MatrixHistogramProps extends MatrixHistogramBasicProps {
- legendPosition?: Position;
- scaleType?: ScaleType;
- showLegend?: boolean;
- showSpacer?: boolean;
- timelineId?: string;
- yTickFormatter?: (value: number) => string;
-}
-
-export interface BarchartConfigs {
- series: {
- xScaleType: ScaleType;
- yScaleType: ScaleType;
- stackAccessors: string[];
- };
- axis: {
- xTickFormatter: TickFormatter;
- yTickFormatter: TickFormatter;
- tickSize: number;
- };
- settings: {
- legendPosition: Position;
- onBrushEnd: UpdateDateRange;
- showLegend: boolean;
- showLegendExtra: boolean;
- theme: {
- scales: {
- barsPadding: number;
- };
- chartMargins: {
- left: number;
- right: number;
- top: number;
- bottom: number;
- };
- chartPaddings: {
- left: number;
- right: number;
- top: number;
- bottom: number;
- };
- };
- };
- customHeight: number;
}
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts
deleted file mode 100644
index 860ec4239073e..0000000000000
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.test.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import {
- getBarchartConfigs,
- DEFAULT_CHART_HEIGHT,
- DEFAULT_Y_TICK_FORMATTER,
- formatToChartDataItem,
- getCustomChartData,
-} from './utils';
-import type { UpdateDateRange } from '../charts/common';
-import { Position } from '@elastic/charts';
-import type { MatrixHistogramData } from '../../../../common/search_strategy';
-import type { BarchartConfigs } from './types';
-
-describe('utils', () => {
- describe('getBarchartConfigs', () => {
- describe('it should get correct default values', () => {
- let configs: BarchartConfigs;
- beforeAll(() => {
- configs = getBarchartConfigs({
- from: '2020-07-07T08:20:18.966Z',
- to: '2020-07-08T08:20:18.966Z',
- onBrushEnd: jest.fn() as UpdateDateRange,
- });
- });
-
- test('it should set default chartHeight', () => {
- expect(configs.customHeight).toEqual(DEFAULT_CHART_HEIGHT);
- });
-
- test('it should show legend by default', () => {
- expect(configs.settings.showLegend).toEqual(true);
- });
-
- test('it should put legend on the right', () => {
- expect(configs.settings.legendPosition).toEqual(Position.Right);
- });
-
- test('it should format Y tick to local string', () => {
- expect(configs.axis.yTickFormatter).toEqual(DEFAULT_Y_TICK_FORMATTER);
- });
- });
-
- describe('it should set custom configs', () => {
- let configs: BarchartConfigs;
- const mockYTickFormatter = jest.fn();
- const mockChartHeight = 100;
-
- beforeAll(() => {
- configs = getBarchartConfigs({
- chartHeight: mockChartHeight,
- from: '2020-07-07T08:20:18.966Z',
- to: '2020-07-08T08:20:18.966Z',
- onBrushEnd: jest.fn() as UpdateDateRange,
- yTickFormatter: mockYTickFormatter,
- showLegend: false,
- });
- });
-
- test('it should set custom chart height', () => {
- expect(configs.customHeight).toEqual(mockChartHeight);
- });
-
- test('it should hide legend', () => {
- expect(configs.settings.showLegend).toEqual(false);
- });
-
- test('it should format y tick with custom formatter', () => {
- expect(configs.axis.yTickFormatter).toEqual(mockYTickFormatter);
- });
- });
- });
-
- describe('formatToChartDataItem', () => {
- test('it should format data correctly', () => {
- const data: [string, MatrixHistogramData[]] = [
- 'g1',
- [
- { x: 1, y: 2, g: 'g1' },
- { x: 2, y: 4, g: 'g1' },
- { x: 3, y: 6, g: 'g1' },
- ],
- ];
- const result = formatToChartDataItem(data);
- expect(result).toEqual({
- key: 'g1',
- value: [
- { x: 1, y: 2, g: 'g1' },
- { x: 2, y: 4, g: 'g1' },
- { x: 3, y: 6, g: 'g1' },
- ],
- });
- });
- });
-
- describe('getCustomChartData', () => {
- test('should handle the case when no data provided', () => {
- const data = null;
- const result = getCustomChartData(data);
-
- expect(result).toEqual([]);
- });
-
- test('should format data correctly', () => {
- const data = [
- { x: 1, y: 2, g: 'g1' },
- { x: 2, y: 4, g: 'g1' },
- { x: 3, y: 6, g: 'g1' },
- { x: 1, y: 1, g: 'g2' },
- { x: 2, y: 3, g: 'g2' },
- { x: 3, y: 5, g: 'g2' },
- ];
- const result = getCustomChartData(data);
-
- expect(result).toEqual([
- {
- key: 'g1',
- color: '#1EA593',
- value: [
- { x: 1, y: 2, g: 'g1' },
- { x: 2, y: 4, g: 'g1' },
- { x: 3, y: 6, g: 'g1' },
- ],
- },
- {
- key: 'g2',
- color: '#2B70F7',
- value: [
- { x: 1, y: 1, g: 'g2' },
- { x: 2, y: 3, g: 'g2' },
- { x: 3, y: 5, g: 'g2' },
- ],
- },
- ]);
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts
index 98b90e52a79c8..7c5f01d26ebdb 100644
--- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts
+++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/utils.ts
@@ -5,72 +5,9 @@
* 2.0.
*/
-import { ScaleType, Position } from '@elastic/charts';
-import { get, groupBy, map, toPairs } from 'lodash/fp';
-
-import type { UpdateDateRange, ChartSeriesData } from '../charts/common';
-import type { MatrixHistogramMappingTypes, BarchartConfigs } from './types';
-import type { MatrixHistogramData } from '../../../../common/search_strategy';
-import { histogramDateTimeFormatter } from '../utils';
-
-interface GetBarchartConfigsProps {
- chartHeight?: number;
- from: string;
- legendPosition?: Position;
- to: string;
- onBrushEnd: UpdateDateRange;
- yTickFormatter?: (value: number) => string;
- showLegend?: boolean;
-}
-
export const DEFAULT_CHART_HEIGHT = 174;
export const DEFAULT_Y_TICK_FORMATTER = (value: string | number): string => value.toLocaleString();
-export const getBarchartConfigs = ({
- chartHeight,
- from,
- legendPosition,
- to,
- onBrushEnd,
- yTickFormatter,
- showLegend,
-}: GetBarchartConfigsProps): BarchartConfigs => ({
- series: {
- xScaleType: ScaleType.Time,
- yScaleType: ScaleType.Linear,
- stackAccessors: ['g'],
- },
- axis: {
- xTickFormatter: histogramDateTimeFormatter([from, to]),
- yTickFormatter: yTickFormatter != null ? yTickFormatter : DEFAULT_Y_TICK_FORMATTER,
- tickSize: 8,
- },
- settings: {
- legendPosition: legendPosition ?? Position.Right,
- onBrushEnd,
- showLegend: showLegend ?? true,
- showLegendExtra: true,
- theme: {
- scales: {
- barsPadding: 0.08,
- },
- chartMargins: {
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
- },
- chartPaddings: {
- left: 0,
- right: 0,
- top: 0,
- bottom: 0,
- },
- },
- },
- customHeight: chartHeight ?? DEFAULT_CHART_HEIGHT,
-});
-
export const defaultLegendColors = [
'#1EA593',
'#2B70F7',
@@ -84,25 +21,3 @@ export const defaultLegendColors = [
'#34130C',
'#GGGGGG',
];
-
-export const formatToChartDataItem = ([key, value]: [
- string,
- MatrixHistogramData[]
-]): ChartSeriesData => ({
- key,
- value,
-});
-
-export const getCustomChartData = (
- data: MatrixHistogramData[] | null,
- mapping?: MatrixHistogramMappingTypes
-): ChartSeriesData[] => {
- if (!data) return [];
- const dataGroupedByEvent = groupBy('g', data);
- const dataGroupedEntries = toPairs(dataGroupedByEvent);
- const formattedChartData = map(formatToChartDataItem, dataGroupedEntries);
- return formattedChartData.map((item: ChartSeriesData, idx: number) => {
- const mapItem = get(item.key, mapping);
- return { ...item, color: mapItem?.color ?? defaultLegendColors[idx] };
- });
-};
diff --git a/x-pack/plugins/security_solution/public/common/components/page/manage_query.tsx b/x-pack/plugins/security_solution/public/common/components/page/manage_query.tsx
index ce8c9d3f68175..5640566d5b92d 100644
--- a/x-pack/plugins/security_solution/public/common/components/page/manage_query.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/page/manage_query.tsx
@@ -7,10 +7,8 @@
import type { Position } from '@elastic/charts';
import { omit } from 'lodash/fp';
-import type { MutableRefObject } from 'react';
import React, { useEffect } from 'react';
-import type { ISessionService } from '@kbn/data-plugin/public';
import type { inputsModel } from '../../store';
import type { GlobalTimeArgs } from '../../containers/use_global_time';
import type { InputsModelId } from '../../store/inputs/constants';
@@ -23,14 +21,13 @@ export interface OwnProps extends Pick;
}
export function manageQuery(
WrappedComponent: React.ComponentClass | React.ComponentType
): React.FC {
const ManageQuery = (props: OwnProps & T) => {
- const { deleteQuery, id, inspect = null, loading, refetch, setQuery, session } = props;
+ const { deleteQuery, id, inspect = null, loading, refetch, setQuery } = props;
useQueryInspector({
deleteQuery,
@@ -38,7 +35,6 @@ export function manageQuery(
loading,
queryId: id,
refetch,
- session,
setQuery,
});
@@ -57,7 +53,6 @@ interface UseQueryInspectorTypes extends Pick;
}
export const useQueryInspector = ({
@@ -67,7 +62,6 @@ export const useQueryInspector = ({
inspect,
loading,
queryId,
- session,
}: UseQueryInspectorTypes) => {
useEffect(() => {
setQuery({
@@ -75,9 +69,8 @@ export const useQueryInspector = ({
inspect: inspect ?? null,
loading,
refetch,
- searchSessionId: session?.current.start(),
});
- }, [deleteQuery, setQuery, queryId, refetch, inspect, loading, session]);
+ }, [deleteQuery, setQuery, queryId, refetch, inspect, loading]);
useEffect(() => {
return () => {
diff --git a/x-pack/plugins/security_solution/public/common/components/page/use_refetch_by_session.tsx b/x-pack/plugins/security_solution/public/common/components/page/use_refetch_by_session.tsx
index 862014ba3e16e..f4557a002004a 100644
--- a/x-pack/plugins/security_solution/public/common/components/page/use_refetch_by_session.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/page/use_refetch_by_session.tsx
@@ -19,13 +19,11 @@ import type { Refetch } from '../../store/inputs/model';
interface UseRefetchByRestartingSessionProps {
inputId?: InputsModelId;
queryId: string;
- skip?: boolean;
}
export const useRefetchByRestartingSession = ({
inputId,
queryId,
- skip,
}: UseRefetchByRestartingSessionProps): {
session: MutableRefObject;
refetchByRestartingSession: Refetch;
@@ -56,10 +54,10 @@ export const useRefetchByRestartingSession = ({
* like most of our components, it refetches when receiving a new search
* session ID.
**/
- searchSessionId: skip ? undefined : searchSessionId,
+ searchSessionId,
})
);
- }, [dispatch, queryId, selectedInspectIndex, skip]);
+ }, [dispatch, queryId, selectedInspectIndex]);
/**
* This is for refetching alert index when the first rule just created
diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx
index 0dbd55b9bae34..42ee8b8ed32f6 100644
--- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.tsx
@@ -139,15 +139,11 @@ const TopNComponent: React.FC = ({
/>
) : (
)}
diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts
index a8f7c55fe5ad0..e154c05e765f0 100644
--- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts
+++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/histogram_configs.ts
@@ -10,7 +10,6 @@ import type {
MatrixHistogramOption,
MatrixHistogramConfigs,
} from '../../../components/matrix_histogram/types';
-import { MatrixHistogramType } from '../../../../../common/search_strategy/security_solution/matrix_histogram';
export const anomaliesStackByOptions: MatrixHistogramOption[] = [
{
@@ -24,9 +23,7 @@ const DEFAULT_STACK_BY = i18n.ANOMALIES_STACK_BY_JOB_ID;
export const histogramConfigs: MatrixHistogramConfigs = {
defaultStackByOption:
anomaliesStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? anomaliesStackByOptions[0],
- errorMessage: i18n.ERROR_FETCHING_ANOMALIES_DATA,
hideHistogramIfEmpty: true,
- histogramType: MatrixHistogramType.anomalies,
stackByOptions: anomaliesStackByOptions,
subtitle: undefined,
title: i18n.ANOMALIES_TITLE,
diff --git a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx
index c3eaf31e2d292..bf662e9fe3cc2 100644
--- a/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/containers/anomalies/anomalies_query_tab_body/index.tsx
@@ -60,7 +60,6 @@ const AnomaliesQueryTabBodyComponent: React.FC = ({
endDate={endDate}
filterQuery={mergedFilterQuery}
id={ID}
- indexNames={indexNames}
setQuery={setQuery}
startDate={startDate}
{...histogramConfigs}
diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts
deleted file mode 100644
index fab3766d2c5d0..0000000000000
--- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts
+++ /dev/null
@@ -1,264 +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, act } from '@testing-library/react-hooks';
-import { useKibana } from '../../lib/kibana';
-import { useMatrixHistogram, useMatrixHistogramCombined } from '.';
-import { MatrixHistogramType } from '../../../../common/search_strategy';
-import { TestProviders } from '../../mock/test_providers';
-import { useTrackHttpRequest } from '../../lib/apm/use_track_http_request';
-
-jest.mock('../../lib/kibana');
-jest.mock('../../lib/apm/use_track_http_request');
-
-const mockEndTracking = jest.fn();
-const mockStartTracking = jest.fn(() => ({ endTracking: mockEndTracking }));
-(useTrackHttpRequest as jest.Mock).mockReturnValue({
- startTracking: mockStartTracking,
-});
-
-const basicResponse = {
- isPartial: false,
- isRunning: false,
- total: 0,
- loaded: 0,
- rawResponse: {
- took: 1,
- timed_out: false,
- hits: {
- max_score: 0,
- hits: [],
- total: 0,
- },
- },
-};
-
-describe('useMatrixHistogram', () => {
- const props = {
- endDate: new Date(Date.now()).toISOString(),
- errorMessage: '',
- filterQuery: {},
- histogramType: MatrixHistogramType.events,
- indexNames: [],
- stackByField: 'event.module',
- startDate: new Date(Date.now()).toISOString(),
- skip: false,
- };
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('should update request when props has changed', async () => {
- const localProps = { ...props };
- const { rerender } = renderHook(() => useMatrixHistogram(localProps), {
- wrapper: TestProviders,
- });
-
- localProps.stackByField = 'event.action';
-
- rerender();
-
- const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls;
-
- expect(mockCalls.length).toBe(2);
- expect(mockCalls[0][0].stackByField).toBe('event.module');
- expect(mockCalls[1][0].stackByField).toBe('event.action');
- });
-
- it('returns a memoized value', async () => {
- const { result, rerender } = renderHook(() => useMatrixHistogram(props), {
- wrapper: TestProviders,
- });
-
- const result1 = result.current[1];
- act(() => rerender());
- const result2 = result.current[1];
-
- expect(result1).toBe(result2);
- });
-
- it("returns buckets for histogram Type 'events'", async () => {
- const localProps = { ...props, histogramType: MatrixHistogramType.events };
- const mockEventsSearchStrategyResponse = {
- ...basicResponse,
- rawResponse: {
- ...basicResponse.rawResponse,
- aggregations: {
- eventActionGroup: {
- doc_count_error_upper_bound: 0,
- sum_other_doc_count: 0,
- buckets: [
- {
- key: 'my dsn test buckets',
- doc_count: 1,
- },
- ],
- },
- },
- },
- };
-
- (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({
- subscribe: ({ next }: { next: Function }) => next(mockEventsSearchStrategyResponse),
- });
-
- const {
- result: { current },
- } = renderHook(() => useMatrixHistogram(localProps), {
- wrapper: TestProviders,
- });
-
- expect(current[1].buckets).toBe(
- mockEventsSearchStrategyResponse.rawResponse.aggregations?.eventActionGroup.buckets
- );
- });
-
- it("returns buckets for histogram Type 'dns'", async () => {
- const mockDnsSearchStrategyResponse = {
- ...basicResponse,
- rawResponse: {
- ...basicResponse.rawResponse,
- aggregations: {
- dns_name_query_count: {
- doc_count_error_upper_bound: 0,
- sum_other_doc_count: 0,
- buckets: [
- {
- key: 'my dsn test buckets',
- doc_count: 1,
- },
- ],
- },
- },
- },
- };
-
- const localProps = { ...props, histogramType: MatrixHistogramType.dns };
- (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({
- subscribe: ({ next }: { next: Function }) => next(mockDnsSearchStrategyResponse),
- });
-
- const {
- result: { current },
- } = renderHook(() => useMatrixHistogram(localProps), {
- wrapper: TestProviders,
- });
-
- expect(current[1].buckets).toBe(
- mockDnsSearchStrategyResponse.rawResponse.aggregations?.dns_name_query_count.buckets
- );
- });
-
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = { ...props };
- const { rerender } = renderHook(() => useMatrixHistogram(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(3);
- });
-
- describe('trackHttpRequest', () => {
- it('should start tracking when request starts', () => {
- renderHook(useMatrixHistogram, {
- initialProps: props,
- wrapper: TestProviders,
- });
-
- expect(mockStartTracking).toHaveBeenCalledWith({
- name: `securitySolutionUI matrixHistogram ${MatrixHistogramType.events}`,
- });
- });
-
- it('should end tracking success when the request succeeds', () => {
- (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({
- subscribe: ({ next }: { next: Function }) => next(basicResponse),
- });
-
- renderHook(useMatrixHistogram, {
- initialProps: props,
- wrapper: TestProviders,
- });
-
- expect(mockEndTracking).toHaveBeenCalledWith('success');
- });
-
- it('should end tracking error when the request fails', () => {
- (useKibana().services.data.search.search as jest.Mock).mockReturnValueOnce({
- subscribe: ({ error }: { error: Function }) => error('some error'),
- });
-
- renderHook(useMatrixHistogram, {
- initialProps: props,
- wrapper: TestProviders,
- });
-
- expect(mockEndTracking).toHaveBeenCalledWith('error');
- });
- });
-});
-
-describe('useMatrixHistogramCombined', () => {
- const props = {
- endDate: new Date(Date.now()).toISOString(),
- errorMessage: '',
- filterQuery: {},
- histogramType: MatrixHistogramType.events,
- indexNames: [],
- stackByField: 'event.module',
- startDate: new Date(Date.now()).toISOString(),
- };
-
- afterEach(() => {
- (useKibana().services.data.search.search as jest.Mock).mockClear();
- });
-
- it('should update request when props has changed', async () => {
- const localProps = { ...props };
- const { rerender } = renderHook(() => useMatrixHistogramCombined(localProps), {
- wrapper: TestProviders,
- });
-
- localProps.stackByField = 'event.action';
-
- rerender();
-
- const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls;
-
- expect(mockCalls.length).toBe(2);
- expect(mockCalls[0][0].stackByField).toBe('event.module');
- expect(mockCalls[1][0].stackByField).toBe('event.action');
- });
-
- it('should do two request when stacking by ip field', async () => {
- const localProps = { ...props, stackByField: 'source.ip' };
- renderHook(() => useMatrixHistogramCombined(localProps), {
- wrapper: TestProviders,
- });
-
- const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls;
-
- expect(mockCalls.length).toBe(2);
- expect(mockCalls[0][0].stackByField).toBe('source.ip');
- expect(mockCalls[1][0].stackByField).toBe('source.ip');
- });
-
- it('returns a memoized value', async () => {
- const { result, rerender } = renderHook(() => useMatrixHistogramCombined(props), {
- wrapper: TestProviders,
- });
-
- const result1 = result.current[1];
- act(() => rerender());
- const result2 = result.current[1];
-
- expect(result1).toBe(result2);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts
deleted file mode 100644
index a9ffa6049d9f9..0000000000000
--- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts
+++ /dev/null
@@ -1,300 +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 deepEqual from 'fast-deep-equal';
-import { getOr, noop } from 'lodash/fp';
-import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { MatrixHistogramRequestOptionsInput } from '../../../../common/api/search_strategy';
-import type { MatrixHistogramQueryProps } from '../../components/matrix_histogram/types';
-import type { inputsModel } from '../../store';
-import { createFilter } from '../helpers';
-import { useKibana } from '../../lib/kibana';
-import type {
- MatrixHistogramStrategyResponse,
- MatrixHistogramData,
-} from '../../../../common/search_strategy/security_solution';
-import {
- MatrixHistogramQuery,
- MatrixHistogramTypeToAggName,
-} from '../../../../common/search_strategy/security_solution';
-import { getInspectResponse } from '../../../helpers';
-import type { InspectResponse } from '../../../types';
-import * as i18n from './translations';
-import { useAppToasts } from '../../hooks/use_app_toasts';
-import { useTrackHttpRequest } from '../../lib/apm/use_track_http_request';
-import { APP_UI_ID } from '../../../../common/constants';
-
-export type Buckets = Array<{
- key: string;
- doc_count: number;
-}>;
-
-const bucketEmpty: Buckets = [];
-
-export interface UseMatrixHistogramArgs {
- data: MatrixHistogramData[];
- inspect: InspectResponse;
- refetch: inputsModel.Refetch;
- totalCount: number;
- buckets: Array<{
- key: string;
- doc_count: number;
- }>;
-}
-
-export const useMatrixHistogram = ({
- endDate,
- errorMessage,
- filterQuery,
- histogramType,
- indexNames,
- isPtrIncluded,
- onError,
- stackByField,
- runtimeMappings,
- startDate,
- threshold,
- skip = false,
- includeMissingData = true,
-}: MatrixHistogramQueryProps): [
- boolean,
- UseMatrixHistogramArgs,
- (to: string, from: string) => void
-] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const { startTracking } = useTrackHttpRequest();
-
- const [matrixHistogramRequest, setMatrixHistogramRequest] =
- useState({
- defaultIndex: indexNames,
- factoryQueryType: MatrixHistogramQuery,
- filterQuery: createFilter(filterQuery),
- histogramType: histogramType ?? histogramType,
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- stackByField,
- runtimeMappings,
- threshold,
- ...(isPtrIncluded != null ? { isPtrIncluded } : {}),
- ...(includeMissingData != null ? { includeMissingData } : {}),
- });
- const { addError } = useAppToasts();
-
- const [matrixHistogramResponse, setMatrixHistogramResponse] = useState({
- data: [],
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: refetch.current,
- totalCount: -1,
- buckets: [],
- });
-
- const search = useCallback(
- (request: MatrixHistogramRequestOptionsInput) => {
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
- const { endTracking } = startTracking({
- name: `${APP_UI_ID} matrixHistogram ${histogramType}`,
- });
-
- searchSubscription$.current = data.search
- .search(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- const histogramBuckets: Buckets = getOr(
- bucketEmpty,
- MatrixHistogramTypeToAggName[histogramType],
- response.rawResponse
- );
- setLoading(false);
- setMatrixHistogramResponse((prevResponse) => ({
- ...prevResponse,
- data: response.matrixHistogramData,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- totalCount: histogramBuckets.reduce((acc, bucket) => bucket.doc_count + acc, 0),
- buckets: histogramBuckets,
- }));
- endTracking('success');
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: errorMessage ?? i18n.FAIL_MATRIX_HISTOGRAM,
- });
- endTracking('error');
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, histogramType, addError, errorMessage, startTracking]
- );
-
- useEffect(() => {
- setMatrixHistogramRequest((prevRequest) => {
- const myRequest = {
- ...prevRequest,
- defaultIndex: indexNames,
- filterQuery: createFilter(filterQuery),
- histogramType,
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- stackByField,
- threshold,
- ...(isPtrIncluded != null ? { isPtrIncluded } : {}),
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [
- indexNames,
- endDate,
- filterQuery,
- startDate,
- stackByField,
- histogramType,
- threshold,
- isPtrIncluded,
- ]);
-
- useEffect(() => {
- // We want to search if it is not skipped, stackByField ends with ip and include missing data
- if (!skip) {
- search(matrixHistogramRequest);
- }
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [matrixHistogramRequest, search, skip]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- const runMatrixHistogramSearch = useCallback(
- (to: string, from: string) => {
- search({
- ...matrixHistogramRequest,
- timerange: {
- interval: '12h',
- from,
- to,
- },
- });
- },
- [matrixHistogramRequest, search]
- );
-
- return [loading, matrixHistogramResponse, runMatrixHistogramSearch];
-};
-
-/* function needed to split ip histogram data requests due to elasticsearch bug https://github.com/elastic/kibana/issues/89205
- * using includeMissingData parameter to do the "missing data" query separately
- **/
-export const useMatrixHistogramCombined = (
- matrixHistogramQueryProps: MatrixHistogramQueryProps
-): [boolean, UseMatrixHistogramArgs] => {
- const [mainLoading, mainResponse] = useMatrixHistogram({
- ...matrixHistogramQueryProps,
- includeMissingData: true,
- });
-
- const skipMissingData = useMemo(
- () => !matrixHistogramQueryProps.stackByField.endsWith('.ip'),
- [matrixHistogramQueryProps.stackByField]
- );
- const [missingDataLoading, missingDataResponse] = useMatrixHistogram({
- ...matrixHistogramQueryProps,
- includeMissingData: false,
- skip:
- skipMissingData ||
- matrixHistogramQueryProps.filterQuery === undefined ||
- matrixHistogramQueryProps.skip,
- });
-
- const combinedLoading = useMemo(
- () => mainLoading || missingDataLoading,
- [mainLoading, missingDataLoading]
- );
-
- const combinedResponse = useMemo(() => {
- if (skipMissingData) return mainResponse;
-
- const { data, inspect, totalCount, refetch, buckets } = mainResponse;
- const {
- data: extraData,
- inspect: extraInspect,
- totalCount: extraTotalCount,
- refetch: extraRefetch,
- } = missingDataResponse;
-
- const combinedRefetch = () => {
- refetch();
- extraRefetch();
- };
-
- if (combinedLoading) {
- return {
- data: [],
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: combinedRefetch,
- totalCount: -1,
- buckets: [],
- };
- }
-
- return {
- data: [...data, ...extraData],
- inspect: {
- dsl: [...inspect.dsl, ...extraInspect.dsl],
- response: [...inspect.response, ...extraInspect.response],
- },
- totalCount: totalCount + extraTotalCount,
- refetch: combinedRefetch,
- buckets,
- };
- }, [combinedLoading, mainResponse, missingDataResponse, skipMissingData]);
-
- return [combinedLoading, combinedResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/translations.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/translations.ts
deleted file mode 100644
index e4ce0c27ae4c3..0000000000000
--- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_MATRIX_HISTOGRAM = i18n.translate(
- 'xpack.securitySolution.matrixHistogram.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on matrix histogram search`,
- }
-);
-
-export const FAIL_MATRIX_HISTOGRAM = i18n.translate(
- 'xpack.securitySolution.matrixHistogram.failSearchDescription',
- {
- defaultMessage: `Failed to run search on matrix histogram`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/common/lib/kuery/index.test.ts b/x-pack/plugins/security_solution/public/common/lib/kuery/index.test.ts
index 1a3cfe0eb9f08..095f49d2f1e0e 100644
--- a/x-pack/plugins/security_solution/public/common/lib/kuery/index.test.ts
+++ b/x-pack/plugins/security_solution/public/common/lib/kuery/index.test.ts
@@ -6,7 +6,8 @@
*/
import expect from '@kbn/expect';
-import { convertToBuildEsQuery } from '.';
+import type { DataProvider } from '../../../../common/types/timeline';
+import { convertToBuildEsQuery, buildGlobalQuery } from '.';
import { mockIndexPattern } from '../../mock';
describe('convertToBuildEsQuery', () => {
@@ -277,3 +278,29 @@ describe('convertToBuildEsQuery', () => {
});
});
});
+
+describe('buildGlobalQuery', () => {
+ it('should generate correct kql query when provided value is an array', () => {
+ const providers = [
+ {
+ and: [],
+ enabled: true,
+ id: 'event-details-value-default-draggable-plain-column-renderer-formatted-field-value-timeline-1-Imhtx44Bu3sCtYk3xxsO-host_name-p1',
+ name: 'p1',
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: 'host.name',
+ value: ['p1', 'p2'],
+ operator: 'includes',
+ displayField: 'host.name',
+ displayValue: '( p1 OR p2 )',
+ },
+ } as DataProvider,
+ ];
+
+ const query = buildGlobalQuery(providers, {});
+
+ expect(query).to.equal('host.name : (p1 OR p2)');
+ });
+});
diff --git a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts
index 80cf74a8f55b7..487917f381b9b 100644
--- a/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts
+++ b/x-pack/plugins/security_solution/public/common/lib/kuery/index.ts
@@ -136,7 +136,7 @@ const buildQueryMatch = (
? convertDateFieldToQuery(dataProvider.queryMatch.field, dataProvider.queryMatch.value)
: `${dataProvider.queryMatch.field} : ${
Array.isArray(dataProvider.queryMatch.value)
- ? dataProvider.queryMatch.value
+ ? `(${dataProvider.queryMatch.value.join(' OR ')})`
: prepareKQLParam(dataProvider.queryMatch.value)
}`
: checkIfFieldTypeIsNested(dataProvider.queryMatch.field, browserFields)
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_histogram.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_histogram.tsx
index 487fc3a4a4e29..77236be454004 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_histogram.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_creation_ui/components/rule_preview/preview_histogram.tsx
@@ -101,11 +101,11 @@ const PreviewHistogramComponent = ({
const previousPreviewId = usePrevious(previewId);
const previewQueryId = `${ID}-${previewId}`;
const previewEmbeddableId = `${previewQueryId}-embeddable`;
- const { responses: visualizationResponse } = useVisualizationResponse({
+ const { responses: visualizationResponses } = useVisualizationResponse({
visualizationId: previewEmbeddableId,
});
- const totalCount = visualizationResponse?.[0]?.hits?.total ?? 0;
+ const totalCount = visualizationResponses?.[0]?.hits?.total ?? 0;
useEffect(() => {
if (previousPreviewId !== previewId && totalCount > 0) {
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx
index 62bac514beb86..e99e6d6659034 100644
--- a/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx
+++ b/x-pack/plugins/security_solution/public/detection_engine/rule_details_ui/pages/rule_details/index.tsx
@@ -724,11 +724,9 @@ const RuleDetailsPageComponent: React.FC = ({
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/chart_content.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/chart_content.tsx
deleted file mode 100644
index 5e0077918b717..0000000000000
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/chart_content.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-import React from 'react';
-import type { VisualizationEmbeddableProps } from '../../../../common/components/visualization_actions/types';
-import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
-import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types';
-import { AlertsCount } from './alerts_count';
-import type { AlertsCountAggregation } from './types';
-
-type ChartContentProps = {
- isChartEmbeddablesEnabled: boolean;
-} & VisualizationEmbeddableProps & {
- isLoadingAlerts: boolean;
- alertsData: AlertSearchResponse | null;
- stackByField0: string;
- stackByField1: string | undefined;
- };
-
-const ChartContentComponent = ({
- alertsData,
- extraActions,
- extraOptions,
- getLensAttributes,
- height,
- id,
- inspectTitle,
- isChartEmbeddablesEnabled,
- isLoadingAlerts,
- scopeId,
- stackByField0,
- stackByField1,
- timerange,
-}: ChartContentProps) => {
- return isChartEmbeddablesEnabled ? (
-
- ) : alertsData != null ? (
-
- ) : null;
-};
-
-export const ChartContent = React.memo(ChartContentComponent);
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx
index 6b0b0ed9a0e08..9451ce6ca3b27 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx
@@ -6,8 +6,9 @@
*/
import React from 'react';
-import { waitFor, act } from '@testing-library/react';
+import { act } from '@testing-library/react';
import { mount } from 'enzyme';
+import type { Action } from '@kbn/ui-actions-plugin/public';
import { AlertsCountPanel } from '.';
import type { Status } from '../../../../../common/api/detection_engine';
@@ -17,7 +18,7 @@ import { TestProviders } from '../../../../common/mock';
import { ChartContextMenu } from '../../../pages/detection_engine/chart_panels/chart_context_menu';
import { TABLE } from '../../../pages/detection_engine/chart_panels/chart_select/translations';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
-import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable';
+import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
import type { ExperimentalFeatures } from '../../../../../common/experimental_features';
import { allowedExperimentalValues } from '../../../../../common/experimental_features';
@@ -39,24 +40,9 @@ jest.mock('react-router-dom', () => {
return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) };
});
-const defaultUseQueryAlertsReturn = {
- loading: false,
- data: {},
- setQuery: () => {},
- response: '',
- request: '',
- refetch: () => {},
-};
-const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn);
-jest.mock('../../../containers/detection_engine/alerts/use_query', () => {
- return {
- useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props),
- };
-});
-
jest.mock('../../../../common/hooks/use_experimental_features');
jest.mock('../../../../common/components/page/use_refetch_by_session');
-jest.mock('../../../../common/components/visualization_actions/lens_embeddable');
+jest.mock('../../../../common/components/visualization_actions/visualization_embeddable');
jest.mock('../../../../common/components/page/use_refetch_by_session');
jest.mock('../common/hooks', () => ({
useInspectButton: jest.fn(),
@@ -80,6 +66,7 @@ const defaultProps = {
showBuildingBlockAlerts: false,
showOnlyThreatIndicatorAlerts: false,
status: 'open' as Status,
+ extraActions: [{ id: 'resetGroupByFields' }] as Action[],
};
const mockSetToggle = jest.fn();
const mockUseQueryToggle = useQueryToggle as jest.Mock;
@@ -90,7 +77,6 @@ describe('AlertsCountPanel', () => {
mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle });
mockUseIsExperimentalFeatureEnabled.mockImplementation(
getMockUseIsExperimentalFeatureEnabled({
- chartEmbeddablesEnabled: false,
alertsPageChartsEnabled: false,
})
);
@@ -160,26 +146,6 @@ describe('AlertsCountPanel', () => {
});
});
- describe('Query', () => {
- it('it render with a illegal KQL', async () => {
- jest.mock('@kbn/es-query', () => ({
- buildEsQuery: jest.fn().mockImplementation(() => {
- throw new Error('Something went wrong');
- }),
- }));
- const props = { ...defaultProps, query: { query: 'host.name: "', language: 'kql' } };
- const wrapper = mount(
-
-
-
- );
-
- await waitFor(() => {
- expect(wrapper.find('[data-test-subj="alertsCountPanel"]').exists()).toBeTruthy();
- });
- });
- });
-
describe('toggleQuery', () => {
it('toggles', async () => {
await act(async () => {
@@ -199,7 +165,7 @@ describe('AlertsCountPanel', () => {
);
- expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(true);
});
});
it('alertsPageChartsEnabled is false and toggleStatus=false, hide', async () => {
@@ -210,14 +176,13 @@ describe('AlertsCountPanel', () => {
);
- expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(false);
});
});
it('alertsPageChartsEnabled is true and isExpanded=true, render', async () => {
mockUseIsExperimentalFeatureEnabled.mockImplementation(
getMockUseIsExperimentalFeatureEnabled({
- chartEmbeddablesEnabled: false,
alertsPageChartsEnabled: true,
})
);
@@ -227,13 +192,12 @@ describe('AlertsCountPanel', () => {
);
- expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(true);
});
});
it('alertsPageChartsEnabled is true and isExpanded=false, hide', async () => {
mockUseIsExperimentalFeatureEnabled.mockImplementation(
getMockUseIsExperimentalFeatureEnabled({
- chartEmbeddablesEnabled: false,
alertsPageChartsEnabled: true,
})
);
@@ -243,54 +207,61 @@ describe('AlertsCountPanel', () => {
);
- expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toEqual(false);
});
});
});
-});
-describe('when the isChartEmbeddablesEnabled experimental feature flag is enabled', () => {
- beforeEach(() => {
- jest.clearAllMocks();
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle });
- mockUseIsExperimentalFeatureEnabled.mockImplementation(
- getMockUseIsExperimentalFeatureEnabled({
- chartEmbeddablesEnabled: true,
- alertsPageChartsEnabled: false,
- })
- );
- });
+ describe('Visualization', () => {
+ it('should render embeddable', async () => {
+ await act(async () => {
+ const wrapper = mount(
+
+
+
+ );
+ expect(wrapper.find('[data-test-subj="visualization-embeddable"]').exists()).toBeTruthy();
+ });
+ });
- it('renders LensEmbeddable', async () => {
- await act(async () => {
- const wrapper = mount(
-
-
-
- );
- expect(wrapper.find('[data-test-subj="embeddable-count-table"]').exists()).toBeTruthy();
+ it('should render with provided height', async () => {
+ await act(async () => {
+ mount(
+
+
+
+ );
+ expect((VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual(
+ 218
+ );
+ });
});
- });
- it('renders LensEmbeddable with provided height', async () => {
- await act(async () => {
- mount(
-
-
-
- );
- expect((LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual(218);
+ it('should render with extra actions', async () => {
+ await act(async () => {
+ mount(
+
+
+
+ );
+ expect(
+ (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraActions[0].id
+ ).toEqual('resetGroupByFields');
+ });
});
- });
- it('should skip calling getAlertsRiskQuery', async () => {
- await act(async () => {
- mount(
-
-
-
- );
- expect(mockUseQueryAlerts.mock.calls[0][0].skip).toBeTruthy();
+ it('should render with extra options', async () => {
+ await act(async () => {
+ mount(
+
+
+
+ );
+ expect(
+ (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraOptions
+ .breakdownField
+ ).toEqual(defaultProps.stackByField1);
+ });
});
});
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
index ee3a5b12e8fee..4b5efb9ae8dd1 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx
@@ -5,33 +5,25 @@
* 2.0.
*/
-import { EuiProgress } from '@elastic/eui';
import type { EuiComboBox } from '@elastic/eui';
import type { Action } from '@kbn/ui-actions-plugin/public';
-import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
-import React, { memo, useMemo, useEffect, useCallback } from 'react';
+import React, { memo, useMemo, useCallback } from 'react';
import { v4 as uuidv4 } from 'uuid';
-import type { Filter, Query } from '@kbn/es-query';
-import { buildEsQuery } from '@kbn/es-query';
+import type { Filter } from '@kbn/es-query';
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { HeaderSection } from '../../../../common/components/header_section';
-import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query';
-import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants';
import { InspectButtonContainer } from '../../../../common/components/inspect';
-import { getAlertsCountQuery } from './helpers';
import * as i18n from './translations';
-import type { AlertsCountAggregation } from './types';
import { KpiPanel } from '../common/components';
-import { useInspectButton } from '../common/hooks';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { FieldSelection } from '../../../../common/components/field_selection';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { getAlertsTableLensAttributes as getLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/alerts/alerts_table';
import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
-import { ChartContent } from './chart_content';
+import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
export const DETECTIONS_ALERTS_COUNT_ID = 'detections-alerts-count';
@@ -42,13 +34,10 @@ interface AlertsCountPanelProps {
filters?: Filter[];
inspectTitle: string;
panelHeight?: number;
- query?: Query;
- runtimeMappings?: MappingRuntimeFields;
setStackByField0: (stackBy: string) => void;
setStackByField0ComboboxInputRef?: (inputRef: HTMLInputElement | null) => void;
setStackByField1: (stackBy: string | undefined) => void;
setStackByField1ComboboxInputRef?: (inputRef: HTMLInputElement | null) => void;
- signalIndexName: string | null;
stackByField0: string;
stackByField0ComboboxRef?: React.RefObject>;
stackByField1: string | undefined;
@@ -68,13 +57,10 @@ export const AlertsCountPanel = memo(
filters,
inspectTitle,
panelHeight,
- query,
- runtimeMappings,
setStackByField0,
setStackByField0ComboboxInputRef,
setStackByField1,
setStackByField1ComboboxInputRef,
- signalIndexName,
stackByField0,
stackByField0ComboboxRef,
stackByField1,
@@ -84,29 +70,11 @@ export const AlertsCountPanel = memo(
isExpanded,
setIsExpanded,
}) => {
- const { to, from, deleteQuery, setQuery } = useGlobalTime();
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
+ const { to, from } = useGlobalTime();
const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled');
// create a unique, but stable (across re-renders) query id
const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_COUNT_ID}-${uuidv4()}`, []);
- // Disabling the fecth method in useQueryAlerts since it is defaulted to the old one
- // const fetchMethod = fetchQueryRuleRegistryAlerts;
-
- const additionalFilters = useMemo(() => {
- try {
- return [
- buildEsQuery(
- undefined,
- query != null ? [query] : [],
- filters?.filter((f) => f.meta.disabled === false) ?? []
- ),
- ];
- } catch (e) {
- return [];
- }
- }, [query, filters]);
-
const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTIONS_ALERTS_COUNT_ID);
const toggleQuery = useCallback(
(newToggleStatus: boolean) => {
@@ -119,11 +87,6 @@ export const AlertsCountPanel = memo(
[setToggleStatus, setIsExpanded, isAlertsPageChartsEnabled]
);
- const querySkip = useMemo(
- () => (isAlertsPageChartsEnabled ? !isExpanded : !toggleStatus),
- [isAlertsPageChartsEnabled, isExpanded, toggleStatus]
- );
-
const timerange = useMemo(() => ({ from, to }), [from, to]);
const extraVisualizationOptions = useMemo(
@@ -133,57 +96,7 @@ export const AlertsCountPanel = memo(
}),
[filters, stackByField1]
);
- const {
- loading: isLoadingAlerts,
- data: alertsData,
- setQuery: setAlertsQuery,
- response,
- request,
- refetch,
- } = useQueryAlerts<{}, AlertsCountAggregation>({
- query: getAlertsCountQuery({
- stackByField0,
- stackByField1,
- from,
- to,
- additionalFilters,
- runtimeMappings,
- }),
- indexName: signalIndexName,
- skip: querySkip || isChartEmbeddablesEnabled,
- queryName: ALERTS_QUERY_NAMES.COUNT,
- });
- useEffect(() => {
- setAlertsQuery(
- getAlertsCountQuery({
- additionalFilters,
- from,
- runtimeMappings,
- stackByField0,
- stackByField1,
- to,
- })
- );
- }, [
- additionalFilters,
- from,
- runtimeMappings,
- setAlertsQuery,
- stackByField0,
- stackByField1,
- to,
- ]);
-
- useInspectButton({
- deleteQuery,
- loading: isLoadingAlerts,
- refetch,
- request,
- response,
- setQuery,
- uniqueQueryId,
- });
const showCount = useMemo(() => {
if (isAlertsPageChartsEnabled) {
return isExpanded;
@@ -214,9 +127,6 @@ export const AlertsCountPanel = memo(
toggleQuery={toggleQuery}
>
(
stackByField1ComboboxRef={stackByField1ComboboxRef}
stackByWidth={stackByWidth}
uniqueQueryId={uniqueQueryId}
- useLensCompatibleFields={isChartEmbeddablesEnabled}
+ useLensCompatibleFields={true}
/>
- {showCount &&
- (isLoadingAlerts ? (
-
- ) : (
-
- ))}
+ {showCount && (
+
+ )}
);
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx
index 0a426edb33cb8..6b92dd328f625 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx
@@ -11,17 +11,12 @@ import { mount } from 'enzyme';
import type { Filter } from '@kbn/es-query';
import { SecurityPageName } from '../../../../app/types';
-import { CHART_SETTINGS_POPOVER_ARIA_LABEL } from '../../../../common/components/chart_settings_popover/translations';
-import { DEFAULT_WIDTH } from '../../../../common/components/charts/draggable_legend';
-import { MatrixLoader } from '../../../../common/components/matrix_histogram/matrix_loader';
-import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1 } from '../common/config';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { TestProviders } from '../../../../common/mock';
-import * as helpers from './helpers';
import { mockAlertSearchResponse } from './mock_data';
-import { ChartContextMenu } from '../../../pages/detection_engine/chart_panels/chart_context_menu';
-import { AlertsHistogramPanel, LEGEND_WITH_COUNTS_WIDTH } from '.';
-import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable';
+import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable';
+
+import { AlertsHistogramPanel } from '.';
import type { ExperimentalFeatures } from '../../../../../common';
import { allowedExperimentalValues } from '../../../../../common';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
@@ -81,30 +76,20 @@ jest.mock('../../../../common/lib/kibana', () => {
};
});
-jest.mock('../../../../common/components/navigation/use_url_state_query_params');
+jest.mock('../../../../common/components/visualization_actions/visualization_embeddable');
-const defaultUseQueryAlertsReturn = {
- loading: true,
- setQuery: () => undefined,
- data: null,
- response: '',
- request: '',
- refetch: null,
-};
-const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn);
+jest.mock('../../../../common/hooks/use_experimental_features');
-jest.mock('../../../containers/detection_engine/alerts/use_query', () => {
- const original = jest.requireActual('../../../containers/detection_engine/alerts/use_query');
+jest.mock('../../../../common/components/visualization_actions/use_visualization_response', () => {
+ const original = jest.requireActual(
+ '../../../../common/components/visualization_actions/use_visualization_response'
+ );
return {
...original,
- useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props),
+ useVisualizationResponse: jest.fn().mockReturnValue({ loading: false }),
};
});
-jest.mock('../../../../common/hooks/use_experimental_features');
-jest.mock('../../../../common/components/page/use_refetch_by_session');
-jest.mock('../../../../common/components/visualization_actions/lens_embeddable');
-jest.mock('../../../../common/components/page/use_refetch_by_session');
jest.mock('../common/hooks', () => {
const actual = jest.requireActual('../common/hooks');
return {
@@ -115,7 +100,6 @@ jest.mock('../common/hooks', () => {
const mockUseIsExperimentalFeatureEnabled = jest.fn((feature: keyof ExperimentalFeatures) => {
if (feature === 'alertsPageChartsEnabled') return false;
- if (feature === 'chartEmbeddablesEnabled') return false;
return allowedExperimentalValues[feature];
});
@@ -146,6 +130,7 @@ const defaultProps = {
};
const mockSetToggle = jest.fn();
const mockUseQueryToggle = useQueryToggle as jest.Mock;
+const mockUseVisualizationResponse = useVisualizationResponse as jest.Mock;
describe('AlertsHistogramPanel', () => {
beforeEach(() => {
@@ -157,7 +142,7 @@ describe('AlertsHistogramPanel', () => {
);
});
- it('renders correctly', () => {
+ test('renders correctly', () => {
const wrapper = mount(
@@ -169,13 +154,9 @@ describe('AlertsHistogramPanel', () => {
describe('legend counts', () => {
beforeEach(() => {
- mockUseQueryAlerts.mockReturnValue({
+ mockUseVisualizationResponse.mockReturnValue({
loading: false,
- data: mockAlertSearchResponse,
- setQuery: () => {},
- response: '',
- request: '',
- refetch: () => {},
+ responses: mockAlertSearchResponse,
});
});
@@ -188,16 +169,6 @@ describe('AlertsHistogramPanel', () => {
expect(wrapper.find('[data-test-subj="legendItemCount"]').exists()).toBe(false);
});
-
- test('it renders counts in the legend when `showCountsInLegend` is true', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="legendItemCount"]').exists()).toBe(true);
- });
});
test('it renders the header with the specified `alignHeader` alignment', () => {
@@ -212,41 +183,6 @@ describe('AlertsHistogramPanel', () => {
).toContain('flexEnd');
});
- describe('inspect button', () => {
- test('it renders the inspect button by default', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="inspect-icon-button"]').first().exists()).toBe(true);
- });
-
- test('it does NOT render the inspect button when a `chartOptionsContextMenu` is provided', async () => {
- const chartOptionsContextMenu = (queryId: string) => (
-
- );
-
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="inspect-icon-button"]').first().exists()).toBe(false);
- });
- });
-
test('it aligns the panel flex group at flex start to ensure the context menu is displayed at the top of the panel', () => {
const wrapper = mount(
@@ -263,13 +199,9 @@ describe('AlertsHistogramPanel', () => {
const onFieldSelected = jest.fn();
const optionToSelect = 'agent.hostname';
- mockUseQueryAlerts.mockReturnValue({
+ mockUseVisualizationResponse.mockReturnValue({
loading: false,
- data: mockAlertSearchResponse,
- setQuery: () => {},
- response: '',
- request: '',
- refetch: () => {},
+ responses: mockAlertSearchResponse,
});
render(
@@ -411,67 +343,6 @@ describe('AlertsHistogramPanel', () => {
});
});
- test('it renders the chart options context menu when a `chartOptionsContextMenu` is provided', async () => {
- const chartOptionsContextMenu = (queryId: string) => (
-
- );
-
- render(
-
-
-
- );
-
- expect(
- screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL })
- ).toBeInTheDocument();
- });
-
- describe('legend width', () => {
- beforeEach(() => {
- mockUseQueryAlerts.mockReturnValue({
- loading: false,
- data: mockAlertSearchResponse,
- setQuery: () => {},
- response: '',
- request: '',
- refetch: () => {},
- });
- });
-
- test('it renders the legend with the expected default min-width', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
- 'min-width',
- `${DEFAULT_WIDTH}px`
- );
- });
-
- test('it renders the legend with the expected min-width when `showCountsInLegend` is true', () => {
- const wrapper = mount(
-
-
-
- );
-
- expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
- 'min-width',
- `${LEGEND_WITH_COUNTS_WIDTH}px`
- );
- });
- });
-
describe('Button view alerts', () => {
it('renders correctly', () => {
const props = { ...defaultProps, showLinkToAlerts: true };
@@ -532,47 +403,8 @@ describe('AlertsHistogramPanel', () => {
});
});
- describe('CombinedQueries', () => {
- it('combinedQueries props is valid, alerts query include combinedQueries', async () => {
- const mockGetAlertsHistogramQuery = jest.spyOn(helpers, 'getAlertsHistogramQuery');
-
- const props = {
- ...defaultProps,
- query: { query: 'host.name: "', language: 'kql' },
- combinedQueries:
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"exists":{"field":"process.name"}}],"should":[],"must_not":[]}}',
- };
- const wrapper = mount(
-
-
-
- );
-
- await waitFor(() => {
- expect(mockGetAlertsHistogramQuery.mock.calls[0]).toEqual([
- 'kibana.alert.rule.name',
- '2020-07-07T08:20:18.966Z',
- '2020-07-08T08:20:18.966Z',
- [
- {
- bool: {
- filter: [{ match_all: {} }, { exists: { field: 'process.name' } }],
- must: [],
- must_not: [],
- should: [],
- },
- },
- ],
- undefined,
- ]);
- });
- wrapper.unmount();
- });
- });
-
describe('Filters', () => {
it('filters props is valid, alerts query include filter', async () => {
- const mockGetAlertsHistogramQuery = jest.spyOn(helpers, 'getAlertsHistogramQuery');
const statusFilter: Filter = {
meta: {
alias: null,
@@ -593,7 +425,6 @@ describe('AlertsHistogramPanel', () => {
const props = {
...defaultProps,
- query: { query: '', language: 'kql' },
filters: [statusFilter],
};
const wrapper = mount(
@@ -603,109 +434,20 @@ describe('AlertsHistogramPanel', () => {
);
await waitFor(() => {
- expect(mockGetAlertsHistogramQuery.mock.calls[1]).toEqual([
- 'kibana.alert.rule.name',
- '2020-07-07T08:20:18.966Z',
- '2020-07-08T08:20:18.966Z',
- [
- {
- bool: {
- filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }],
- must: [],
- must_not: [],
- should: [],
- },
- },
- ],
- undefined,
- ]);
+ expect(
+ (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].timerange
+ ).toEqual({
+ from: '2020-07-07T08:20:18.966Z',
+ to: '2020-07-08T08:20:18.966Z',
+ });
+ expect(
+ (VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraOptions.filters
+ ).toEqual(props.filters);
});
wrapper.unmount();
});
});
- describe('parseCombinedQueries', () => {
- it('return empty object when variables is undefined', async () => {
- expect(helpers.parseCombinedQueries(undefined)).toEqual({});
- });
-
- it('return empty object when variables is empty string', async () => {
- expect(helpers.parseCombinedQueries('')).toEqual({});
- });
-
- it('return empty object when variables is NOT a valid stringify json object', async () => {
- expect(helpers.parseCombinedQueries('hello world')).toEqual({});
- });
-
- it('return a valid json object when variables is a valid json stringify', async () => {
- expect(
- helpers.parseCombinedQueries(
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"exists":{"field":"process.name"}}],"should":[],"must_not":[]}}'
- )
- ).toMatchInlineSnapshot(`
- Object {
- "bool": Object {
- "filter": Array [
- Object {
- "match_all": Object {},
- },
- Object {
- "exists": Object {
- "field": "process.name",
- },
- },
- ],
- "must": Array [],
- "must_not": Array [],
- "should": Array [],
- },
- }
- `);
- });
- });
-
- describe('buildCombinedQueries', () => {
- it('return empty array when variables is undefined', async () => {
- expect(helpers.buildCombinedQueries(undefined)).toEqual([]);
- });
-
- it('return empty array when variables is empty string', async () => {
- expect(helpers.buildCombinedQueries('')).toEqual([]);
- });
-
- it('return array with empty object when variables is NOT a valid stringify json object', async () => {
- expect(helpers.buildCombinedQueries('hello world')).toEqual([{}]);
- });
-
- it('return a valid json object when variables is a valid json stringify', async () => {
- expect(
- helpers.buildCombinedQueries(
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"exists":{"field":"process.name"}}],"should":[],"must_not":[]}}'
- )
- ).toMatchInlineSnapshot(`
- Array [
- Object {
- "bool": Object {
- "filter": Array [
- Object {
- "match_all": Object {},
- },
- Object {
- "exists": Object {
- "field": "process.name",
- },
- },
- ],
- "must": Array [],
- "must_not": Array [],
- "should": Array [],
- },
- },
- ]
- `);
- });
- });
-
describe('toggleQuery', () => {
it('toggles', async () => {
await act(async () => {
@@ -722,7 +464,6 @@ describe('AlertsHistogramPanel', () => {
describe('when alertsPageChartsEnabled = false', () => {
beforeEach(() => {
jest.clearAllMocks();
- mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(false); // for chartEmbeddablesEnabled flag
mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(false); // for alertsPageChartsEnabled flag
});
@@ -734,7 +475,10 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ true
+ );
});
});
it('toggleStatus=false, hide', async () => {
@@ -745,7 +489,10 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ false
+ );
});
});
});
@@ -753,7 +500,6 @@ describe('AlertsHistogramPanel', () => {
describe('when alertsPageChartsEnabled = true', () => {
beforeEach(() => {
jest.clearAllMocks();
- mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(false); // for chartEmbeddablesEnabled flag
mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(true); // for alertsPageChartsEnabled flag
});
@@ -764,7 +510,10 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ true
+ );
});
});
it('isExpanded=false, hide', async () => {
@@ -774,7 +523,10 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ false
+ );
});
});
it('isExpanded is not passed in and toggleStatus =true, render', async () => {
@@ -784,7 +536,10 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(true);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ true
+ );
});
});
it('isExpanded is not passed in and toggleStatus =false, hide', async () => {
@@ -795,17 +550,19 @@ describe('AlertsHistogramPanel', () => {
);
- expect(wrapper.find(MatrixLoader).exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="panelFlexGroup"]').exists()).toEqual(false);
+ expect(wrapper.find('[data-test-subj="embeddable-matrix-histogram"]').exists()).toEqual(
+ false
+ );
});
});
});
});
- describe('when isChartEmbeddablesEnabled = true', () => {
+ describe('VisualizationEmbeddable', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle });
- mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(true); // for chartEmbeddablesEnabled flag
mockUseIsExperimentalFeatureEnabled.mockReturnValueOnce(false); // for alertsPageChartsEnabled flag
});
@@ -816,13 +573,14 @@ describe('AlertsHistogramPanel', () => {
);
- mockUseQueryAlerts.mockReturnValue({
+ mockUseVisualizationResponse.mockReturnValue({
loading: false,
- setQuery: () => undefined,
- data: null,
- response: '',
- request: '',
- refetch: null,
+ responses: [
+ {
+ hits: { total: 0 },
+ aggregations: { myAgg: { buckets: [{ key: 'A' }, { key: 'B' }, { key: 'C' }] } },
+ },
+ ],
});
wrapper.setProps({ filters: [] });
wrapper.update();
@@ -850,18 +608,9 @@ describe('AlertsHistogramPanel', () => {
);
- expect((LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual(155);
- });
- });
-
- it('should skip calling getAlertsRiskQuery', async () => {
- await act(async () => {
- mount(
-
-
-
+ expect((VisualizationEmbeddable as unknown as jest.Mock).mock.calls[0][0].height).toEqual(
+ 155
);
- expect(mockUseQueryAlerts.mock.calls[0][0].skip).toBeTruthy();
});
});
diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
index 11b1cd68f859b..69066b3d3e595 100644
--- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx
@@ -5,9 +5,7 @@
* 2.0.
*/
-import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
import type { Action } from '@kbn/ui-actions-plugin/public';
-import type { Position } from '@elastic/charts';
import type { EuiComboBox, EuiTitleSize } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui';
import React, { memo, useCallback, useMemo, useState, useEffect } from 'react';
@@ -16,41 +14,26 @@ import { isEmpty, noop } from 'lodash/fp';
import { v4 as uuidv4 } from 'uuid';
import { sumBy } from 'lodash';
-import type { Filter, Query } from '@kbn/es-query';
-import { buildEsQuery } from '@kbn/es-query';
-import { getEsQueryConfig } from '@kbn/data-plugin/common';
+import type { Filter } from '@kbn/es-query';
+
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { APP_UI_ID } from '../../../../../common/constants';
import type { UpdateDateRange } from '../../../../common/components/charts/common';
-import type { LegendItem } from '../../../../common/components/charts/draggable_legend_item';
-import { escapeDataProviderId } from '../../../../common/components/drag_and_drop/helpers';
import { HeaderSection } from '../../../../common/components/header_section';
-import { useQueryAlerts } from '../../../containers/detection_engine/alerts/use_query';
-import { ALERTS_QUERY_NAMES } from '../../../containers/detection_engine/alerts/constants';
import { getDetectionEngineUrl, useFormatUrl } from '../../../../common/components/link_to';
-import { defaultLegendColors } from '../../../../common/components/matrix_histogram/utils';
-import { InspectButtonContainer } from '../../../../common/components/inspect';
-import { MatrixLoader } from '../../../../common/components/matrix_histogram/matrix_loader';
import { useKibana } from '../../../../common/lib/kibana';
import {
- parseCombinedQueries,
- buildCombinedQueries,
- formatAlertsData,
- getAlertsHistogramQuery,
showInitialLoadingSpinner,
createGenericSubtitle,
createEmbeddedDataSubtitle,
} from './helpers';
-import { AlertsHistogram } from './alerts_histogram';
import * as i18n from './translations';
-import type { AlertsAggregation, AlertsTotal } from './types';
import { LinkButton } from '../../../../common/components/links';
import { SecurityPageName } from '../../../../app/types';
import { DEFAULT_STACK_BY_FIELD, PANEL_HEIGHT } from '../common/config';
import type { AlertsStackByField } from '../common/types';
import { KpiPanel, StackByComboBox } from '../common/components';
-import { useInspectButton } from '../common/hooks';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
import { GROUP_BY_TOP_LABEL } from '../common/translations';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
@@ -60,48 +43,30 @@ import { VisualizationEmbeddable } from '../../../../common/components/visualiza
import { useAlertHistogramCount } from '../../../hooks/alerts_visualization/use_alert_histogram_count';
import { useVisualizationResponse } from '../../../../common/components/visualization_actions/use_visualization_response';
-const defaultTotalAlertsObj: AlertsTotal = {
- value: 0,
- relation: 'eq',
-};
-
export const DETECTIONS_HISTOGRAM_ID = 'detections-histogram';
const ViewAlertsFlexItem = styled(EuiFlexItem)`
margin-left: ${({ theme }) => theme.eui.euiSizeL};
`;
-const OptionsFlexItem = styled(EuiFlexItem)`
- margin-left: ${({ theme }) => theme.eui.euiSizeS};
-`;
-
-export const LEGEND_WITH_COUNTS_WIDTH = 300; // px
-
const CHART_HEIGHT = 155; // px
interface AlertsHistogramPanelProps {
alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd';
chartHeight?: number;
- chartOptionsContextMenu?: (queryId: string) => React.ReactNode;
- combinedQueries?: string;
comboboxRef?: React.RefObject>;
defaultStackByOption?: string;
extraActions?: Action[];
filters?: Filter[];
headerChildren?: React.ReactNode;
inspectTitle?: React.ReactNode;
- legendPosition?: Position;
onFieldSelected?: (field: string) => void;
/** Override all defaults, and only display this field */
onlyField?: AlertsStackByField;
paddingSize?: 's' | 'm' | 'l' | 'none';
panelHeight?: number;
- query?: Query;
- runtimeMappings?: MappingRuntimeFields;
setComboboxInputRef?: (inputRef: HTMLInputElement | null) => void;
- showCountsInLegend?: boolean;
showGroupByPlaceholder?: boolean;
- showLegend?: boolean;
showLinkToAlerts?: boolean;
showStackBy?: boolean;
showTotalAlertsCount?: boolean;
@@ -117,57 +82,42 @@ interface AlertsHistogramPanelProps {
setIsExpanded?: (status: boolean) => void;
}
-const NO_LEGEND_DATA: LegendItem[] = [];
-
export const AlertsHistogramPanel = memo(
({
alignHeader,
chartHeight = CHART_HEIGHT,
- chartOptionsContextMenu,
- combinedQueries,
comboboxRef,
defaultStackByOption = DEFAULT_STACK_BY_FIELD,
extraActions,
filters,
headerChildren,
inspectTitle,
- legendPosition = 'right',
onFieldSelected,
onlyField,
paddingSize = 'm',
panelHeight = PANEL_HEIGHT,
- query,
- runtimeMappings,
setComboboxInputRef,
- showCountsInLegend = false,
showGroupByPlaceholder = false,
- showLegend = true,
showLinkToAlerts = false,
showStackBy = true,
showTotalAlertsCount = false,
- signalIndexName,
stackByLabel,
stackByWidth,
- timelineId,
title = i18n.HISTOGRAM_HEADER,
titleSize = 'm',
- updateDateRange,
hideQueryToggle = false,
isExpanded,
setIsExpanded,
}) => {
- const { to, from, deleteQuery, setQuery } = useGlobalTime();
+ const { to, from } = useGlobalTime();
// create a unique, but stable (across re-renders) query id
const uniqueQueryId = useMemo(() => `${DETECTIONS_HISTOGRAM_ID}-${uuidv4()}`, []);
const visualizationId = `alerts-trend-embeddable-${uniqueQueryId}`;
const [isInitialLoading, setIsInitialLoading] = useState(true);
- const [isInspectDisabled, setIsInspectDisabled] = useState(false);
- const [totalAlertsObj, setTotalAlertsObj] = useState(defaultTotalAlertsObj);
const [selectedStackByOption, setSelectedStackByOption] = useState(
onlyField == null ? defaultStackByOption : onlyField
);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
const isAlertsPageChartsEnabled = useIsExperimentalFeatureEnabled('alertsPageChartsEnabled');
const onSelect = useCallback(
@@ -197,42 +147,16 @@ export const AlertsHistogramPanel = memo(
[setToggleStatus, setIsExpanded, isAlertsPageChartsEnabled]
);
- const querySkip = useMemo(
- () =>
- isAlertsPageChartsEnabled && setIsExpanded !== undefined ? !isExpanded : !toggleStatus,
- [isAlertsPageChartsEnabled, setIsExpanded, isExpanded, toggleStatus]
- );
-
const timerange = useMemo(() => ({ from, to }), [from, to]);
- const {
- loading: isLoadingAlerts,
- data: alertsData,
- setQuery: setAlertsQuery,
- response,
- request,
- refetch,
- } = useQueryAlerts<{}, AlertsAggregation>({
- query: getAlertsHistogramQuery(
- selectedStackByOption,
- from,
- to,
- buildCombinedQueries(combinedQueries),
- runtimeMappings
- ),
- indexName: signalIndexName,
- skip: querySkip || isChartEmbeddablesEnabled,
- queryName: ALERTS_QUERY_NAMES.HISTOGRAM,
- });
-
const kibana = useKibana();
const { navigateToApp } = kibana.services.application;
const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.alerts);
-
+ const { loading: isLoadingAlerts } = useVisualizationResponse({
+ visualizationId,
+ });
const totalAlerts = useAlertHistogramCount({
- totalAlertsObj,
visualizationId,
- isChartEmbeddablesEnabled,
});
const goToDetectionEngine = useCallback(
@@ -245,30 +169,6 @@ export const AlertsHistogramPanel = memo(
},
[navigateToApp, urlSearch]
);
- const formattedAlertsData = useMemo(() => formatAlertsData(alertsData), [alertsData]);
-
- const legendItems: LegendItem[] = useMemo(
- () =>
- showLegend && alertsData?.aggregations?.alertsByGrouping?.buckets != null
- ? alertsData.aggregations.alertsByGrouping.buckets.map((bucket, i) => ({
- color: i < defaultLegendColors.length ? defaultLegendColors[i] : undefined,
- count: showCountsInLegend ? bucket.doc_count : undefined,
- dataProviderId: escapeDataProviderId(
- `draggable-legend-item-${uuidv4()}-${selectedStackByOption}-${bucket.key}`
- ),
- field: selectedStackByOption,
- timelineId,
- value: bucket?.key_as_string ?? bucket.key,
- }))
- : NO_LEGEND_DATA,
- [
- alertsData?.aggregations?.alertsByGrouping.buckets,
- selectedStackByOption,
- showCountsInLegend,
- showLegend,
- timelineId,
- ]
- );
useEffect(() => {
let canceled = false;
@@ -281,60 +181,6 @@ export const AlertsHistogramPanel = memo(
};
}, [isInitialLoading, isLoadingAlerts, setIsInitialLoading]);
- useInspectButton({
- deleteQuery,
- loading: isLoadingAlerts,
- refetch,
- request,
- response,
- setQuery,
- uniqueQueryId,
- });
-
- useEffect(() => {
- setTotalAlertsObj(
- alertsData?.hits.total ?? {
- value: 0,
- relation: 'eq',
- }
- );
- }, [alertsData]);
-
- useEffect(() => {
- try {
- let converted = null;
- if (combinedQueries != null) {
- converted = parseCombinedQueries(combinedQueries);
- } else {
- converted = buildEsQuery(
- undefined,
- query != null ? [query] : [],
- filters?.filter((f) => f.meta.disabled === false) ?? [],
- {
- ...getEsQueryConfig(kibana.services.uiSettings),
- dateFormatTZ: undefined,
- }
- );
- }
- setIsInspectDisabled(false);
- setAlertsQuery(
- getAlertsHistogramQuery(
- selectedStackByOption,
- from,
- to,
- !isEmpty(converted) ? [converted] : [],
- runtimeMappings
- )
- );
- } catch (e) {
- setIsInspectDisabled(true);
- setAlertsQuery(
- getAlertsHistogramQuery(selectedStackByOption, from, to, [], runtimeMappings)
- );
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [selectedStackByOption, from, to, query, filters, combinedQueries, runtimeMappings]);
-
const linkButton = useMemo(() => {
if (showLinkToAlerts) {
return (
@@ -385,114 +231,89 @@ export const AlertsHistogramPanel = memo(
);
const embeddedDataAvailable = !!aggregationBucketsCount;
- const showsEmbeddedData = showHistogram && isChartEmbeddablesEnabled;
- const subtitle = showsEmbeddedData
+ const subtitle = showHistogram
? createEmbeddedDataSubtitle(embeddedDataLoaded, embeddedDataAvailable, totalAlerts)
: createGenericSubtitle(isInitialLoading, showTotalAlertsCount, totalAlerts);
return (
-
-
+
-
-
-
- {showStackBy && (
- <>
-
- {showGroupByPlaceholder && (
- <>
-
-
-
-
- >
- )}
- >
- )}
- {headerChildren != null && headerChildren}
-
- {chartOptionsContextMenu != null && !isChartEmbeddablesEnabled && (
-
- {chartOptionsContextMenu(uniqueQueryId)}
-
+
+
+ {showStackBy && (
+ <>
+
+ {showGroupByPlaceholder && (
+ <>
+
+
+
+
+ >
+ )}
+ >
)}
- {linkButton}
-
-
- {showHistogram ? (
- isChartEmbeddablesEnabled ? (
-
- ) : isInitialLoading ? (
-
- ) : (
-
- )
- ) : null}
-
-
+ {headerChildren != null && headerChildren}
+
+ {linkButton}
+
+
+ {showHistogram ? (
+
+ ) : null}
+
);
}
);
diff --git a/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.test.tsx b/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.test.tsx
index d86b9b37c568f..9781f3ad991e3 100644
--- a/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.test.tsx
@@ -15,16 +15,10 @@ jest.mock('../../../common/components/visualization_actions/use_visualization_re
describe('useAlertHistogramCount', () => {
const props = {
- totalAlertsObj: { value: 10, relation: '' },
visualizationId: 'mockVisualizationId',
- isChartEmbeddablesEnabled: false,
};
- it('returns total alerts count', () => {
- const { result } = renderHook(() => useAlertHistogramCount(props), { wrapper: TestProviders });
- expect(result.current).toEqual('Showing: 10 alerts');
- });
- it('returns visualization alerts count when isChartEmbeddablesEnabled is true', () => {
+ it('returns visualization alerts count', () => {
const testPops = { ...props, isChartEmbeddablesEnabled: true };
const { result } = renderHook(() => useAlertHistogramCount(testPops), {
wrapper: TestProviders,
diff --git a/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.ts b/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.ts
index 39365401a68df..487718b10e193 100644
--- a/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.ts
+++ b/x-pack/plugins/security_solution/public/detections/hooks/alerts_visualization/use_alert_histogram_count.ts
@@ -11,40 +11,25 @@ import numeral from '@elastic/numeral';
import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants';
import { useUiSetting$ } from '../../../common/lib/kibana';
import { SHOWING_ALERTS } from '../../components/alerts_kpis/alerts_histogram_panel/translations';
-import type { AlertsTotal } from '../../components/alerts_kpis/alerts_histogram_panel/types';
import { useVisualizationResponse } from '../../../common/components/visualization_actions/use_visualization_response';
export const useAlertHistogramCount = ({
- totalAlertsObj,
visualizationId,
- isChartEmbeddablesEnabled,
}: {
- totalAlertsObj: AlertsTotal;
visualizationId: string;
- isChartEmbeddablesEnabled: boolean;
}): string => {
const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT);
- const { responses: visualizationResponse } = useVisualizationResponse({ visualizationId });
-
- const totalAlerts = useMemo(
- () =>
- SHOWING_ALERTS(
- numeral(totalAlertsObj.value).format(defaultNumberFormat),
- totalAlertsObj.value,
- totalAlertsObj.relation === 'gte' ? '>' : totalAlertsObj.relation === 'lte' ? '<' : ''
- ),
- [totalAlertsObj.value, totalAlertsObj.relation, defaultNumberFormat]
- );
+ const { responses: visualizationResponses } = useVisualizationResponse({ visualizationId });
const visualizationAlerts = useMemo(() => {
const visualizationAlertsCount =
- visualizationResponse != null ? visualizationResponse[0].hits.total : 0;
+ visualizationResponses != null ? visualizationResponses[0].hits.total : 0;
return SHOWING_ALERTS(
numeral(visualizationAlertsCount).format(defaultNumberFormat),
visualizationAlertsCount,
''
);
- }, [defaultNumberFormat, visualizationResponse]);
+ }, [defaultNumberFormat, visualizationResponses]);
- return isChartEmbeddablesEnabled ? visualizationAlerts : totalAlerts;
+ return visualizationAlerts;
};
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx
index feb8b0ead2be4..6319f4d98420d 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx
@@ -18,6 +18,8 @@ import { TestProviders } from '../../../../common/mock';
import { ChartPanels } from '.';
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
import { useQueryToggle } from '../../../../common/containers/query_toggle';
+import { LensEmbeddable } from '../../../../common/components/visualization_actions/lens_embeddable';
+import { createResetGroupByFieldAction } from '../../../components/alerts_kpis/alerts_histogram_panel/helpers';
jest.mock('./alerts_local_storage');
jest.mock('../../../../common/containers/sourcerer');
@@ -227,79 +229,164 @@ describe('ChartPanels', () => {
describe(`'Reset group by fields' context menu action`, () => {
describe('Group by', () => {
- const alertViewSelections = ['trend', 'table', 'treemap'];
+ test(`it resets the 'Group by' field to the default value, even if the user has triggered validation errors, when 'alertViewSelection' is 'treemap'`, async () => {
+ (useAlertsLocalStorage as jest.Mock).mockReturnValue({
+ ...defaultAlertSettings,
+ alertViewSelection: 'treemap',
+ });
+
+ const defaultValue = 'kibana.alert.rule.name';
+ const invalidValue = 'an invalid value';
+
+ render(
+
+
+
+ );
+
+ const initialInput = screen.getAllByTestId('comboBoxSearchInput')[0];
+ expect(initialInput).toHaveValue(defaultValue);
+
+ // update the EuiComboBox input to an invalid value:
+ fireEvent.change(initialInput, { target: { value: invalidValue } });
+
+ const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[0];
+ expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by' EuiComboBox is now in the "error state"
+ expect(afterInvalidInput).toBeInvalid();
+
+ resetGroupByFields(); // invoke the `Reset group by fields` context menu action
+
+ await waitFor(() => {
+ const afterReset = screen.getAllByTestId('comboBoxSearchInput')[0];
+ expect(afterReset).toHaveValue(defaultValue); // back to the default
+ });
+ });
- alertViewSelections.forEach((alertViewSelection) => {
- test(`it resets the 'Group by' field to the default value, even if the user has triggered validation errors, when 'alertViewSelection' is '${alertViewSelection}'`, async () => {
+ describe.each([['trend'], ['table']])(`when 'alertViewSelection' is '%s'`, (view) => {
+ test(`it has resets the 'Group by' field as an extra action`, async () => {
(useAlertsLocalStorage as jest.Mock).mockReturnValue({
...defaultAlertSettings,
- alertViewSelection,
+ alertViewSelection: view,
});
- const defaultValue = 'kibana.alert.rule.name';
- const invalidValue = 'an invalid value';
+ const mockResetGroupByFieldsAction = [
+ createResetGroupByFieldAction({ callback: jest.fn(), order: 5 }),
+ ];
+
+ const testProps = {
+ ...defaultProps,
+ extraActions: mockResetGroupByFieldsAction,
+ };
render(
-
+
);
- const initialInput = screen.getAllByTestId('comboBoxSearchInput')[0];
- expect(initialInput).toHaveValue(defaultValue);
-
- // update the EuiComboBox input to an invalid value:
- fireEvent.change(initialInput, { target: { value: invalidValue } });
-
- const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[0];
- expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by' EuiComboBox is now in the "error state"
- expect(afterInvalidInput).toBeInvalid();
-
- resetGroupByFields(); // invoke the `Reset group by fields` context menu action
-
await waitFor(() => {
- const afterReset = screen.getAllByTestId('comboBoxSearchInput')[0];
- expect(afterReset).toHaveValue(defaultValue); // back to the default
+ expect(
+ (LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraActions.length
+ ).toEqual(1);
+ expect(
+ (LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraActions[0].id
+ ).toEqual('resetGroupByField');
});
});
});
- });
-
- describe('Group by top', () => {
- const justTableAndTreemap = ['table', 'treemap'];
- justTableAndTreemap.forEach((alertViewSelection) => {
- test(`it resets the 'Group by top' field to the default value, even if the user has triggered validation errors, when 'alertViewSelection' is '${alertViewSelection}'`, async () => {
+ describe.each([
+ ['trend', 'kibana.alert.rule.name'],
+ ['table', 'kibana.alert.rule.name'],
+ ])(`when 'alertViewSelection' is '%s'`, (view, defaultGroupBy) => {
+ test(`it has resets the 'Group by' field as an extra action, with default value ${defaultGroupBy}`, async () => {
(useAlertsLocalStorage as jest.Mock).mockReturnValue({
...defaultAlertSettings,
- alertViewSelection,
+ alertViewSelection: view,
});
- const defaultValue = 'host.name';
- const invalidValue = 'an-invalid-value';
+ const mockResetGroupByFieldsAction = [
+ createResetGroupByFieldAction({ callback: jest.fn(), order: 5 }),
+ ];
+
+ const testProps = {
+ ...defaultProps,
+ extraActions: mockResetGroupByFieldsAction,
+ };
render(
-
+
);
- const initialInput = screen.getAllByTestId('comboBoxSearchInput')[1];
- expect(initialInput).toHaveValue(defaultValue);
+ await waitFor(() => {
+ expect(
+ (LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraActions.length
+ ).toEqual(1);
+ expect(
+ (LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraActions[0].id
+ ).toEqual('resetGroupByField');
+ expect((LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].stackByField).toEqual(
+ defaultGroupBy
+ );
+ });
+ });
+ });
+ });
+
+ describe('Group by top', () => {
+ test(`it resets the 'Group by top' field to the default value, even if the user has triggered validation errors, when 'alertViewSelection' is 'treemap'`, async () => {
+ (useAlertsLocalStorage as jest.Mock).mockReturnValue({
+ ...defaultAlertSettings,
+ alertViewSelection: 'treemap',
+ });
- // update the EuiComboBox input to an invalid value:
- fireEvent.change(initialInput, { target: { value: invalidValue } });
+ const defaultValue = 'host.name';
+ const invalidValue = 'an-invalid-value';
- const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[1];
- expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by top' EuiComboBox is now in the "error state"
- expect(afterInvalidInput).toBeInvalid();
+ render(
+
+
+
+ );
- resetGroupByFields(); // invoke the `Reset group by fields` context menu action
+ const initialInput = screen.getAllByTestId('comboBoxSearchInput')[1];
+ expect(initialInput).toHaveValue(defaultValue);
- await waitFor(() => {
- const afterReset = screen.getAllByTestId('comboBoxSearchInput')[1];
- expect(afterReset).toHaveValue(defaultValue); // back to the default
- });
+ // update the EuiComboBox input to an invalid value:
+ fireEvent.change(initialInput, { target: { value: invalidValue } });
+
+ const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[1];
+ expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by top' EuiComboBox is now in the "error state"
+ expect(afterInvalidInput).toBeInvalid();
+
+ resetGroupByFields(); // invoke the `Reset group by fields` context menu action
+
+ await waitFor(() => {
+ const afterReset = screen.getAllByTestId('comboBoxSearchInput')[1];
+ expect(afterReset).toHaveValue(defaultValue); // back to the default
+ });
+ });
+
+ test(`it renders the 'Group by top' field to the default value, when 'alertViewSelection' is 'table'`, async () => {
+ (useAlertsLocalStorage as jest.Mock).mockReturnValue({
+ ...defaultAlertSettings,
+ alertViewSelection: 'table',
+ });
+
+ const defaultValue = 'host.name';
+
+ render(
+
+
+
+ );
+
+ await waitFor(() => {
+ expect(
+ (LensEmbeddable as unknown as jest.Mock).mock.calls[0][0].extraOptions.breakdownField
+ ).toEqual(defaultValue);
});
});
});
diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx
index 9ab7fc73c3560..86c402b20288c 100644
--- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx
+++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx
@@ -201,7 +201,6 @@ const ChartPanelsComponent: React.FC = ({
= ({
inspectTitle={i18n.TREND}
onFieldSelected={updateCommonStackBy0}
panelHeight={CHART_PANEL_HEIGHT}
- query={query}
- runtimeMappings={runtimeMappings}
setComboboxInputRef={setStackByField0ComboboxInputRef}
- showCountsInLegend={true}
showGroupByPlaceholder={false}
showTotalAlertsCount={false}
signalIndexName={signalIndexName}
@@ -239,13 +235,10 @@ const ChartPanelsComponent: React.FC = ({
filters={alertsDefaultFilters}
inspectTitle={isAlertsPageChartsEnabled ? i18n.COUNTS : i18n.TABLE}
panelHeight={CHART_PANEL_HEIGHT}
- query={query}
- runtimeMappings={runtimeMappings}
setStackByField0={updateCommonStackBy0}
setStackByField0ComboboxInputRef={setStackByField0ComboboxInputRef}
setStackByField1={updateCommonStackBy1}
setStackByField1ComboboxInputRef={setStackByField1ComboboxInputRef}
- signalIndexName={signalIndexName}
stackByField0={countTableStackBy0}
stackByField0ComboboxRef={stackByField0ComboboxRef}
stackByField1={countTableStackBy1}
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.test.ts
deleted file mode 100644
index 394d777b30fd7..0000000000000
--- a/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.test.ts
+++ /dev/null
@@ -1,105 +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 { getAlertsQueryForRiskScore } from './get_alerts_query_for_risk_score';
-import type { RiskStats } from '../../../common/search_strategy/security_solution/risk_score/all';
-import { RiskSeverity } from '../../../common/search_strategy/security_solution/risk_score/all';
-
-const risk: RiskStats = {
- calculated_level: RiskSeverity.critical,
- calculated_score_norm: 70,
- rule_risks: [],
- multipliers: [],
- '@timestamp': '',
- id_field: '',
- id_value: '',
- calculated_score: 0,
- category_1_score: 0,
- category_1_count: 0,
- category_2_score: 0,
- category_2_count: 0,
- notes: [],
- inputs: [],
-};
-
-describe('getAlertsQueryForRiskScore', () => {
- it('should return query from host risk score', () => {
- expect(
- getAlertsQueryForRiskScore({
- riskScore: {
- host: {
- name: 'host-1',
- risk,
- },
- '@timestamp': '2023-08-10T14:00:00.000Z',
- },
- riskRangeStart: 'now-30d',
- })
- ).toEqual({
- _source: false,
- size: 1000,
- fields: ['*'],
- query: {
- bool: {
- filter: [
- { term: { 'host.name': 'host-1' } },
- {
- range: {
- '@timestamp': { gte: '2023-07-11T14:00:00.000Z', lte: '2023-08-10T14:00:00.000Z' },
- },
- },
- ],
- },
- },
- });
- });
-
- it('should return query from user risk score', () => {
- expect(
- getAlertsQueryForRiskScore({
- riskScore: {
- user: {
- name: 'user-1',
- risk,
- },
- '@timestamp': '2023-08-10T14:00:00.000Z',
- },
- riskRangeStart: 'now-30d',
- })
- ).toEqual({
- _source: false,
- size: 1000,
- fields: ['*'],
- query: {
- bool: {
- filter: [
- { term: { 'user.name': 'user-1' } },
- {
- range: {
- '@timestamp': { gte: '2023-07-11T14:00:00.000Z', lte: '2023-08-10T14:00:00.000Z' },
- },
- },
- ],
- },
- },
- });
- });
-
- it('should return query with custom fields', () => {
- const query = getAlertsQueryForRiskScore({
- riskScore: {
- user: {
- name: 'user-1',
- risk,
- },
- '@timestamp': '2023-08-10T14:00:00.000Z',
- },
- riskRangeStart: 'now-30d',
- fields: ['event.category', 'event.action'],
- });
- expect(query.fields).toEqual(['event.category', 'event.action']);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.ts
deleted file mode 100644
index 8a75aa19a227d..0000000000000
--- a/x-pack/plugins/security_solution/public/entity_analytics/common/get_alerts_query_for_risk_score.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 {
- isUserRiskScore,
- RiskScoreFields,
-} from '../../../common/search_strategy/security_solution/risk_score/all';
-import type {
- UserRiskScore,
- HostRiskScore,
-} from '../../../common/search_strategy/security_solution/risk_score/all';
-import { getStartDateFromRiskScore } from './get_start_date_from_risk_score';
-
-const ALERTS_SIZE = 1000;
-
-/**
- * return query to fetch alerts related to the risk score
- */
-export const getAlertsQueryForRiskScore = ({
- riskRangeStart,
- riskScore,
- fields,
-}: {
- riskRangeStart: string;
- riskScore: UserRiskScore | HostRiskScore;
- fields?: string[];
-}) => {
- let entityField: string;
- let entityValue: string;
-
- if (isUserRiskScore(riskScore)) {
- entityField = RiskScoreFields.userName;
- entityValue = riskScore.user.name;
- } else {
- entityField = RiskScoreFields.hostName;
- entityValue = riskScore.host.name;
- }
-
- const from = getStartDateFromRiskScore({
- riskScoreTimestamp: riskScore['@timestamp'],
- riskRangeStart,
- });
-
- const riskScoreTimestamp = riskScore['@timestamp'];
-
- return {
- fields: fields || ['*'],
- size: ALERTS_SIZE,
- _source: false,
- query: {
- bool: {
- filter: [
- { term: { [entityField]: entityValue } },
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: riskScoreTimestamp,
- },
- },
- },
- ],
- },
- },
- };
-};
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
index d5ff5425ccaa6..3aef24bd81c8c 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.test.tsx
@@ -8,14 +8,14 @@
import { fireEvent, render } from '@testing-library/react';
import React from 'react';
import { TestProviders } from '../../../../common/mock';
-import { alertDataMock } from '../mocks';
+import { alertInputDataMock } from '../mocks';
import { ActionColumn } from './action_column';
describe('ActionColumn', () => {
it('renders', () => {
const { getByTestId } = render(
-
+
);
@@ -25,7 +25,7 @@ describe('ActionColumn', () => {
it('toggles the popover when button is clicked', () => {
const { getByRole } = render(
-
+
);
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx
index d993e66a562e9..838a51d066803 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/action_column.tsx
@@ -7,20 +7,20 @@
import { EuiButtonIcon, EuiContextMenu, EuiPopover } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import React, { useCallback, useMemo, useState } from 'react';
-import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
+import React, { useCallback, useState } from 'react';
+import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts';
+
import { useRiskInputActionsPanels } from '../hooks/use_risk_input_actions_panels';
interface ActionColumnProps {
- alert: AlertRawData;
+ input: InputAlert;
}
-export const ActionColumn: React.FC = ({ alert }) => {
+export const ActionColumn: React.FC = ({ input }) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const closePopover = useCallback(() => setIsPopoverOpen(false), []);
const togglePopover = useCallback(() => setIsPopoverOpen((isOpen) => !isOpen), []);
- const alerts = useMemo(() => [alert], [alert]);
- const panels = useRiskInputActionsPanels(alerts, closePopover);
+ const panels = useRiskInputActionsPanels([input], closePopover);
return (
{
- it('renders', () => {
+ it('renders when at least one item is selected', () => {
const { getByTestId } = render(
-
+
);
-
expect(getByTestId('risk-input-utility-bar')).toBeInTheDocument();
});
- it('renders current page message when totalItemCount is 1', () => {
- const { getByTestId } = render(
-
-
-
- );
-
- expect(getByTestId('risk-input-utility-bar')).toHaveTextContent('Showing 1 Risk contribution');
- });
-
- it('renders current page message when totalItemCount is 20', () => {
- const { getByTestId } = render(
-
-
-
- );
-
- expect(getByTestId('risk-input-utility-bar')).toHaveTextContent(
- 'Showing 1-10 of 20 Risk contribution'
- );
- });
-
- it('renders current page message when totalItemCount is 20 and on the second page', () => {
- const { getByTestId } = render(
+ it('is hidden by default', () => {
+ const { queryByTestId } = render(
-
+
);
- expect(getByTestId('risk-input-utility-bar')).toHaveTextContent(
- 'Showing 11-20 of 20 Risk contribution'
- );
+ expect(queryByTestId('risk-input-utility-bar')).toBeNull();
});
it('renders selected risk input message', () => {
const { getByTestId } = render(
);
@@ -100,11 +47,7 @@ describe('RiskInputsUtilityBar', () => {
const { getByRole } = render(
);
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx
index 44837bfc2e0c1..e1131e131963a 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/components/utility_bar.tsx
@@ -7,121 +7,79 @@
import type { FunctionComponent } from 'react';
import React, { useCallback, useState } from 'react';
-import type { Pagination } from '@elastic/eui';
+
import {
EuiButtonEmpty,
EuiContextMenu,
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
- EuiText,
+ EuiSpacer,
useEuiTheme,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import { useRiskInputActionsPanels } from '../hooks/use_risk_input_actions_panels';
-import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
+import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts';
interface Props {
- selectedAlerts: AlertRawData[];
- pagination: Pagination;
+ riskInputs: InputAlert[];
}
-export const RiskInputsUtilityBar: FunctionComponent = React.memo(
- ({ selectedAlerts, pagination }) => {
- const { euiTheme } = useEuiTheme();
- const [isPopoverOpen, setIsPopoverOpen] = useState(false);
- const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]);
- const closePopover = useCallback(() => setIsPopoverOpen(false), []);
- const panels = useRiskInputActionsPanels(selectedAlerts, closePopover);
- const displayedCurrentPage = pagination.pageIndex + 1;
- const pageSize = pagination.pageSize ?? 10;
- const fromItem: number = pagination.pageIndex * pageSize + 1;
- const toItem: number = Math.min(pagination.totalItemCount, pageSize * displayedCurrentPage);
+export const RiskInputsUtilityBar: FunctionComponent = React.memo(({ riskInputs }) => {
+ const { euiTheme } = useEuiTheme();
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const togglePopover = useCallback(() => setIsPopoverOpen(!isPopoverOpen), [isPopoverOpen]);
+ const closePopover = useCallback(() => setIsPopoverOpen(false), []);
+ const panels = useRiskInputActionsPanels(riskInputs, closePopover);
- return (
- <>
-
-
-
- {pagination.totalItemCount <= 1 ? (
-
-
-
- ),
- }}
- />
- ) : (
+ if (riskInputs.length === 0) {
+ return null;
+ }
+ return (
+ <>
+
+
+
+
-
-
- {selectedAlerts.length > 0 && (
-
-
-
- }
- >
-
-
- )}
-
-
- >
- );
- }
-);
+
+ }
+ >
+
+
+
+
+
+ >
+ );
+});
RiskInputsUtilityBar.displayName = 'RiskInputsUtilityBar';
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts
index 87e78f7083add..926db73720aae 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions.ts
@@ -11,16 +11,17 @@ import { get, noop } from 'lodash/fp';
import { AttachmentType } from '@kbn/cases-plugin/common';
import type { CaseAttachmentsWithoutOwner } from '@kbn/cases-plugin/public';
import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils';
+
import { useGlobalTime } from '../../../../common/containers/use_global_time';
import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
import { useAddBulkToTimelineAction } from '../../../../detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline';
import { useKibana } from '../../../../common/lib/kibana/kibana_react';
-import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
+import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts';
/**
* The returned actions only support alerts risk inputs.
*/
-export const useRiskInputActions = (alerts: AlertRawData[], closePopover: () => void) => {
+export const useRiskInputActions = (inputs: InputAlert[], closePopover: () => void) => {
const { from, to } = useGlobalTime();
const timelineAction = useAddBulkToTimelineAction({
localFilters: [],
@@ -36,16 +37,16 @@ export const useRiskInputActions = (alerts: AlertRawData[], closePopover: () =>
const caseAttachments: CaseAttachmentsWithoutOwner = useMemo(
() =>
- alerts.map((alert: AlertRawData) => ({
- alertId: alert._id,
- index: alert._index,
+ inputs.map(({ input, alert }: InputAlert) => ({
+ alertId: input.id,
+ index: input.index,
type: AttachmentType.alert,
rule: {
- id: get(ALERT_RULE_UUID, alert.fields)[0],
- name: get(ALERT_RULE_NAME, alert.fields)[0],
+ id: get(ALERT_RULE_UUID, alert),
+ name: get(ALERT_RULE_NAME, alert),
},
})),
- [alerts]
+ [inputs]
);
return useMemo(
@@ -61,19 +62,19 @@ export const useRiskInputActions = (alerts: AlertRawData[], closePopover: () =>
addToNewTimeline: () => {
telemetry.reportAddRiskInputToTimelineClicked({
- quantity: alerts.length,
+ quantity: inputs.length,
});
closePopover();
timelineAction.onClick(
- alerts.map((alert: AlertRawData) => {
+ inputs.map(({ input }: InputAlert) => {
return {
- _id: alert._id,
- _index: alert._index,
+ _id: input.id,
+ _index: input.index,
data: [],
ecs: {
- _id: alert._id,
- _index: alert._index,
+ _id: input.id,
+ _index: input.index,
},
};
}),
@@ -85,7 +86,7 @@ export const useRiskInputActions = (alerts: AlertRawData[], closePopover: () =>
},
}),
[
- alerts,
+ inputs,
caseAttachments,
closePopover,
createCaseFlyout,
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx
index 49bad231f2767..363edc4df9b1d 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.test.tsx
@@ -12,7 +12,7 @@ import { render } from '@testing-library/react';
import { renderHook } from '@testing-library/react-hooks';
import React from 'react';
import { TestProviders } from '../../../../common/mock';
-import { alertDataMock } from '../mocks';
+import { alertInputDataMock } from '../mocks';
import { useRiskInputActionsPanels } from './use_risk_input_actions_panels';
const casesServiceMock = casesPluginMock.createStartContract();
@@ -47,7 +47,7 @@ const TestMenu = ({ panels }: { panels: EuiContextMenuPanelDescriptor[] }) => (
);
-const customRender = (alerts = [alertDataMock]) => {
+const customRender = (alerts = [alertInputDataMock]) => {
const { result } = renderHook(() => useRiskInputActionsPanels(alerts, () => {}), {
wrapper: TestProviders,
});
@@ -67,7 +67,7 @@ describe('useRiskInputActionsPanels', () => {
});
it('displays number of selected alerts when more than one alert is selected', () => {
- const { getByTestId } = customRender([alertDataMock, alertDataMock]);
+ const { getByTestId } = customRender([alertInputDataMock, alertInputDataMock]);
expect(getByTestId('contextMenuPanelTitle')).toHaveTextContent('2 selected');
});
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx
index 21ce285584856..03b25bc85d8db 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/hooks/use_risk_input_actions_panels.tsx
@@ -15,12 +15,12 @@ import { i18n } from '@kbn/i18n';
import { get } from 'lodash/fp';
import { ALERT_RULE_NAME } from '@kbn/rule-data-utils';
import { useRiskInputActions } from './use_risk_input_actions';
-import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
+import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts';
-export const useRiskInputActionsPanels = (alerts: AlertRawData[], closePopover: () => void) => {
+export const useRiskInputActionsPanels = (inputs: InputAlert[], closePopover: () => void) => {
const { cases: casesService } = useKibana<{ cases?: CasesService }>().services;
const { addToExistingCase, addToNewCaseClick, addToNewTimeline } = useRiskInputActions(
- alerts,
+ inputs,
closePopover
);
const userCasesPermissions = casesService?.helpers.canUseCases([SECURITY_SOLUTION_OWNER]);
@@ -37,21 +37,21 @@ export const useRiskInputActionsPanels = (alerts: AlertRawData[], closePopover:
onClick: addToNewTimeline,
};
- const ruleName = get(['fields', ALERT_RULE_NAME], alerts[0]) ?? [''];
+ const ruleName = get(['alert', ALERT_RULE_NAME], inputs[0]) ?? '';
const title = i18n.translate(
'xpack.securitySolution.flyout.entityDetails.riskInputs.actions.title',
{
defaultMessage: 'Risk input: {description}',
values: {
description:
- alerts.length === 1
- ? ruleName[0]
+ inputs.length === 1
+ ? ruleName
: i18n.translate(
'xpack.securitySolution.flyout.entityDetails.riskInputs.actions.titleDescription',
{
defaultMessage: '{quantity} selected',
values: {
- quantity: alerts.length,
+ quantity: inputs.length,
},
}
),
@@ -96,5 +96,5 @@ export const useRiskInputActionsPanels = (alerts: AlertRawData[], closePopover:
: [timelinePanel],
},
];
- }, [addToExistingCase, addToNewCaseClick, addToNewTimeline, alerts, hasCasesPermissions]);
+ }, [addToExistingCase, addToNewCaseClick, addToNewTimeline, inputs, hasCasesPermissions]);
};
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts
index 90fc667f578d3..c05da8c357762 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/mocks/index.ts
@@ -6,14 +6,22 @@
*/
import { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils';
-import type { AlertRawData } from '../tabs/risk_inputs/risk_inputs_tab';
+import { RiskCategories } from '../../../../../common/entity_analytics/risk_engine';
+import type { InputAlert } from '../../../hooks/use_risk_contributing_alerts';
-export const alertDataMock: AlertRawData = {
+export const alertInputDataMock: InputAlert = {
_id: 'test-id',
- _index: 'test-index',
- fields: {
- [ALERT_RULE_UUID]: ['2e051244-b3c6-4779-a241-e1b4f0beceb9'],
- '@timestamp': ['2023-07-20T20:31:24.896Z'],
- [ALERT_RULE_NAME]: ['Rule Name'],
+ input: {
+ id: 'test-id',
+ index: 'test-index',
+ category: RiskCategories.category_1,
+ description: 'test-description',
+ timestamp: '2023-07-20T20:31:24.896Z',
+ risk_score: 50,
+ contribution_score: 20,
+ },
+ alert: {
+ [ALERT_RULE_UUID]: '2e051244-b3c6-4779-a241-e1b4f0beceb9',
+ [ALERT_RULE_NAME]: 'Rule Name',
},
};
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx
index 0adf0b0639f6b..d9088b5fe5bb9 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx
@@ -5,12 +5,12 @@
* 2.0.
*/
-import { fireEvent, render } from '@testing-library/react';
+import { render } from '@testing-library/react';
import React from 'react';
import { TestProviders } from '../../../../../common/mock';
import { times } from 'lodash/fp';
import { RiskInputsTab } from './risk_inputs_tab';
-import { alertDataMock } from '../../mocks';
+import { alertInputDataMock } from '../../mocks';
import { RiskSeverity } from '../../../../../../common/search_strategy';
import { RiskScoreEntity } from '../../../../../../common/entity_analytics/risk_engine';
@@ -58,7 +58,7 @@ describe('RiskInputsTab', () => {
mockUseRiskContributingAlerts.mockReturnValue({
loading: false,
error: false,
- data: [alertDataMock],
+ data: [alertInputDataMock],
});
mockUseRiskScore.mockReturnValue({
loading: false,
@@ -93,7 +93,7 @@ describe('RiskInputsTab', () => {
it('Renders the context section if enabled and risks contains asset criticality', () => {
mockUseUiSetting.mockReturnValue([true]);
- const riskScorewWithAssetCriticality = {
+ const riskScoreWithAssetCriticality = {
'@timestamp': '2021-08-19T16:00:00.000Z',
user: {
name: 'elastic',
@@ -107,7 +107,7 @@ describe('RiskInputsTab', () => {
mockUseRiskScore.mockReturnValue({
loading: false,
error: false,
- data: [riskScorewWithAssetCriticality],
+ data: [riskScoreWithAssetCriticality],
});
const { queryByTestId } = render(
@@ -116,13 +116,13 @@ describe('RiskInputsTab', () => {
);
- expect(queryByTestId('risk-input-asset-criticality-title')).toBeInTheDocument();
+ expect(queryByTestId('risk-input-contexts-title')).toBeInTheDocument();
});
- it('paginates', () => {
+ it('shows extra alerts contribution message', () => {
const alerts = times(
(number) => ({
- ...alertDataMock,
+ ...alertInputDataMock,
_id: number.toString(),
}),
11
@@ -139,16 +139,12 @@ describe('RiskInputsTab', () => {
data: [riskScore],
});
- const { getAllByTestId, getByLabelText } = render(
+ const { queryByTestId } = render(
);
- expect(getAllByTestId('risk-input-table-description-cell')).toHaveLength(10);
-
- fireEvent.click(getByLabelText('Next page'));
-
- expect(getAllByTestId('risk-input-table-description-cell')).toHaveLength(1);
+ expect(queryByTestId('risk-input-extra-alerts-message')).toBeInTheDocument();
});
});
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx
index 7db6a271e93ab..93156528e80b2 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab.tsx
@@ -5,18 +5,23 @@
* 2.0.
*/
-import type { EuiBasicTableColumn, Pagination } from '@elastic/eui';
-import { EuiSpacer, EuiInMemoryTable, EuiTitle, EuiCallOut, EuiText } from '@elastic/eui';
-import React, { useCallback, useMemo, useState } from 'react';
+import type { EuiBasicTableColumn } from '@elastic/eui';
+import { EuiSpacer, EuiInMemoryTable, EuiTitle, EuiCallOut } from '@elastic/eui';
+import type { ReactNode } from 'react';
+import React, { useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
-import { get } from 'lodash/fp';
-import { ALERT_RULE_NAME } from '@kbn/rule-data-utils';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
+import { ALERT_RULE_NAME } from '@kbn/rule-data-utils';
+
+import { get } from 'lodash/fp';
+import type {
+ InputAlert,
+ UseRiskContributingAlertsResult,
+} from '../../../../hooks/use_risk_contributing_alerts';
+import { useRiskContributingAlerts } from '../../../../hooks/use_risk_contributing_alerts';
import { ENABLE_ASSET_CRITICALITY_SETTING } from '../../../../../../common/constants';
import { PreferenceFormattedDate } from '../../../../../common/components/formatted_date';
-import { ActionColumn } from '../../components/action_column';
-import { RiskInputsUtilityBar } from '../../components/utility_bar';
-import { useRiskContributingAlerts } from '../../../../hooks/use_risk_contributing_alerts';
+
import { useRiskScore } from '../../../../api/hooks/use_risk_score';
import type { HostRiskScore, UserRiskScore } from '../../../../../../common/search_strategy';
import {
@@ -26,25 +31,21 @@ import {
} from '../../../../../../common/search_strategy';
import { RiskScoreEntity } from '../../../../../../common/entity_analytics/risk_engine';
import { AssetCriticalityBadge } from '../../../asset_criticality';
+import { RiskInputsUtilityBar } from '../../components/utility_bar';
+import { ActionColumn } from '../../components/action_column';
export interface RiskInputsTabProps extends Record {
entityType: RiskScoreEntity;
entityName: string;
}
-export interface AlertRawData {
- fields: Record;
- _index: string;
- _id: string;
-}
-
const FIRST_RECORD_PAGINATION = {
cursorStart: 0,
querySize: 1,
};
export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) => {
- const [selectedItems, setSelectedItems] = useState([]);
+ const [selectedItems, setSelectedItems] = useState([]);
const nameFilterQuery = useMemo(() => {
if (entityType === RiskScoreEntity.host) {
@@ -67,24 +68,19 @@ export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) =>
});
const riskScore = riskScoreData && riskScoreData.length > 0 ? riskScoreData[0] : undefined;
- const {
- loading: loadingAlerts,
- data: alertsData,
- error: riskAlertsError,
- } = useRiskContributingAlerts({ riskScore });
+
+ const alerts = useRiskContributingAlerts({ riskScore });
const euiTableSelectionProps = useMemo(
() => ({
- onSelectionChange: (selected: AlertRawData[]) => {
- setSelectedItems(selected);
- },
initialSelected: [],
selectable: () => true,
+ onSelectionChange: setSelectedItems,
}),
[]
);
- const alertsColumns: Array> = useMemo(
+ const inputColumns: Array> = useMemo(
() => [
{
name: (
@@ -94,12 +90,10 @@ export const RiskInputsTab = ({ entityType, entityName }: RiskInputsTabProps) =>
/>
),
width: '80px',
- render: (alert: AlertRawData) => {
- return ;
- },
+ render: (data: InputAlert) => ,
},
{
- field: 'fields.@timestamp',
+ field: 'input.timestamp',
name: (
render: (timestamp: string) => ,
},
{
- field: 'fields',
+ field: 'alert',
'data-test-subj': 'risk-input-table-description-cell',
name: (
truncateText: true,
mobileOptions: { show: true },
sortable: true,
- render: (fields: AlertRawData['fields']) => get(ALERT_RULE_NAME, fields),
+ render: (alert: InputAlert['alert']) => get(ALERT_RULE_NAME, alert),
+ },
+ {
+ field: 'input.contribution_score',
+ 'data-test-subj': 'risk-input-table-contribution-cell',
+ name: (
+
+ ),
+ truncateText: false,
+ mobileOptions: { show: true },
+ sortable: true,
+ align: 'right',
+ render: (contribution: number) => contribution.toFixed(2),
},
],
[]
);
- const [currentPage, setCurrentPage] = useState<{
- index: number;
- size: number;
- }>({ index: 0, size: 10 });
-
- const onTableChange = useCallback(({ page }) => {
- setCurrentPage(page);
- }, []);
-
- const pagination: Pagination = useMemo(
- () => ({
- totalItemCount: alertsData?.length ?? 0,
- pageIndex: currentPage.index,
- pageSize: currentPage.size,
- }),
- [currentPage.index, currentPage.size, alertsData?.length]
- );
-
const [isAssetCriticalityEnabled] = useUiSetting$(ENABLE_ASSET_CRITICALITY_SETTING);
- if (riskScoreError || riskAlertsError) {
+ if (riskScoreError) {
return (
-
-
+
+
+
>
);
return (
<>
{isAssetCriticalityEnabled && (
-
+
)}
+
{riskInputsAlertSection}
>
);
};
-const RiskInputsAssetCriticalitySection: React.FC<{
+RiskInputsTab.displayName = 'RiskInputsTab';
+
+const ContextsSection: React.FC<{
riskScore?: UserRiskScore | HostRiskScore;
loading: boolean;
}> = ({ riskScore, loading }) => {
- const criticalityLevel = useMemo(() => {
+ const criticality = useMemo(() => {
if (!riskScore) {
return undefined;
}
if (isUserRiskScore(riskScore)) {
- return riskScore.user.risk.criticality_level;
+ return {
+ level: riskScore.user.risk.criticality_level,
+ contribution: riskScore.user.risk.category_2_score,
+ };
}
- return riskScore.host.risk.criticality_level;
+ return {
+ level: riskScore.host.risk.criticality_level,
+ contribution: riskScore.host.risk.category_2_score,
+ };
}, [riskScore]);
- if (loading || criticalityLevel === undefined) {
+ if (loading || criticality === undefined) {
return null;
}
return (
<>
-
+
-
-
-
-
-
+ ),
+ value: (
+
+ ),
+ contribution: (criticality.contribution || 0).toFixed(2),
+ },
+ ]}
/>
-
-
>
);
};
-RiskInputsTab.displayName = 'RiskInputsTab';
+interface ContextRow {
+ field: ReactNode;
+ value: ReactNode;
+ contribution: string;
+}
+
+const contextColumns: Array> = [
+ {
+ field: 'field',
+ name: (
+
+ ),
+ width: '30%',
+ render: (field: ContextRow['field']) => field,
+ },
+ {
+ field: 'value',
+ name: (
+
+ ),
+ width: '30%',
+ render: (val: ContextRow['value']) => val,
+ },
+ {
+ field: 'contribution',
+ width: '30%',
+ align: 'right',
+ name: (
+
+ ),
+ render: (score: ContextRow['contribution']) => score,
+ },
+];
+
+interface ExtraAlertsMessageProps {
+ riskScore?: UserRiskScore | HostRiskScore;
+ alerts: UseRiskContributingAlertsResult;
+}
+const ExtraAlertsMessage: React.FC = ({ riskScore, alerts }) => {
+ const totals = !riskScore
+ ? { count: 0, score: 0 }
+ : isUserRiskScore(riskScore)
+ ? { count: riskScore.user.risk.category_1_count, score: riskScore.user.risk.category_1_score }
+ : { count: riskScore.host.risk.category_1_count, score: riskScore.host.risk.category_1_score };
+
+ const displayed = {
+ count: alerts.data?.length || 0,
+ score: alerts.data?.reduce((sum, { input }) => sum + (input.contribution_score || 0), 0) || 0,
+ };
+
+ if (displayed.count >= totals.count) {
+ return null;
+ }
+ return (
+
+ }
+ iconType="annotation"
+ />
+ );
+};
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
index 22458a6d00fd1..d5b35bcef4596 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.test.tsx
@@ -7,13 +7,11 @@
import { render } from '@testing-library/react';
import React from 'react';
-import { RiskScoreOverTime, scoreFormatter } from '.';
+import { RiskScoreOverTime } from '.';
import { TestProviders } from '../../../common/mock';
-import { LineSeries } from '@elastic/charts';
import { RiskScoreEntity } from '../../../../common/search_strategy';
import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
-const mockLineSeries = LineSeries as jest.Mock;
const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock;
jest.mock('@elastic/charts', () => {
const original = jest.requireActual('@elastic/charts');
@@ -58,17 +56,7 @@ describe('Risk Score Over Time', () => {
expect(queryByTestId('RiskScoreOverTime')).toBeInTheDocument();
});
- it('renders loader when loading', () => {
- const { queryByTestId } = render(
-
-
-
- );
-
- expect(queryByTestId('RiskScoreOverTime-loading')).toBeInTheDocument();
- });
-
- it('renders VisualizationEmbeddable when isChartEmbeddablesEnabled = true and spaceId exists', () => {
+ it('renders VisualizationEmbeddable', () => {
mockUseIsExperimentalFeatureEnabled.mockReturnValue(true);
const { queryByTestId } = render(
@@ -79,25 +67,4 @@ describe('Risk Score Over Time', () => {
expect(queryByTestId('visualization-embeddable')).toBeInTheDocument();
});
-
- describe('scoreFormatter', () => {
- it('renders score formatted', () => {
- render(
-
-
-
- );
-
- const tickFormat = mockLineSeries.mock.calls[0][0].tickFormat;
-
- expect(tickFormat).toBe(scoreFormatter);
- });
-
- it('renders a formatted score', () => {
- expect(scoreFormatter(3.000001)).toEqual('3');
- expect(scoreFormatter(3.4999)).toEqual('3');
- expect(scoreFormatter(3.51111)).toEqual('4');
- expect(scoreFormatter(3.9999)).toEqual('4');
- });
- });
});
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
index 210f578b36ad7..b4555af087d51 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
+++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_over_time/index.tsx
@@ -5,38 +5,19 @@
* 2.0.
*/
-import React, { useMemo, useCallback } from 'react';
-import type { TooltipHeaderFormatter } from '@elastic/charts';
-import {
- Chart,
- LineSeries,
- ScaleType,
- Settings,
- Axis,
- Position,
- AnnotationDomainType,
- LineAnnotation,
- Tooltip,
-} from '@elastic/charts';
-import { EuiFlexGroup, EuiFlexItem, EuiLoadingChart, EuiText, EuiPanel } from '@elastic/eui';
-import styled from 'styled-components';
-import { euiThemeVars } from '@kbn/ui-theme';
-import { i18n } from '@kbn/i18n';
-import { chartDefaultSettings, useThemes } from '../../../common/components/charts/common';
-import { useTimeZone } from '../../../common/lib/kibana';
-import { histogramDateTimeFormatter } from '../../../common/components/utils';
+import React, { useMemo } from 'react';
+
+import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
+
import { HeaderSection } from '../../../common/components/header_section';
-import { InspectButton, InspectButtonContainer } from '../../../common/components/inspect';
-import * as translations from './translations';
-import { PreferenceFormattedDate } from '../../../common/components/formatted_date';
+import { InspectButtonContainer } from '../../../common/components/inspect';
+
import type {
HostRiskScore,
RiskScoreEntity,
UserRiskScore,
} from '../../../../common/search_strategy';
-import { isUserRiskScore } from '../../../../common/search_strategy';
import { useSpaceId } from '../../../common/hooks/use_space_id';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
import { getRiskScoreOverTimeAreaAttributes } from '../../lens_attributes/risk_score_over_time_area';
@@ -52,19 +33,7 @@ export interface RiskScoreOverTimeProps {
toggleQuery?: (status: boolean) => void;
}
-const RISKY_THRESHOLD = 70;
-const DEFAULT_CHART_HEIGHT = 250;
const CHART_HEIGHT = 180;
-const StyledEuiText = styled(EuiText)`
- font-size: 9px;
- font-weight: ${({ theme }) => theme.eui.euiFontWeightSemiBold};
- margin-right: ${({ theme }) => theme.eui.euiSizeXS};
-`;
-
-const LoadingChart = styled(EuiLoadingChart)`
- display: block;
- text-align: center;
-`;
export const scoreFormatter = (d: number) => Math.round(d).toString();
@@ -79,27 +48,7 @@ const RiskScoreOverTimeComponent: React.FC = ({
toggleStatus,
toggleQuery,
}) => {
- const timeZone = useTimeZone();
-
- const dataTimeFormatter = useMemo(() => histogramDateTimeFormatter([from, to]), [from, to]);
- const headerFormatter = useCallback(
- ({ value }) => ,
- []
- );
-
- const { baseTheme, theme } = useThemes();
- const graphData = useMemo(
- () =>
- riskScore
- ?.map((data) => ({
- x: data['@timestamp'],
- y: (isUserRiskScore(data) ? data.user : data.host).risk.calculated_score_norm,
- }))
- .reverse() ?? [],
- [riskScore]
- );
const spaceId = useSpaceId();
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
const timerange = useMemo(
() => ({
from,
@@ -119,17 +68,12 @@ const RiskScoreOverTimeComponent: React.FC = ({
toggleStatus={toggleStatus}
/>
- {toggleStatus && !isChartEmbeddablesEnabled && (
-
-
-
- )}
{toggleStatus && (
- {isChartEmbeddablesEnabled && spaceId ? (
+ {spaceId && (
= ({
height={CHART_HEIGHT}
extraOptions={{ spaceId }}
/>
- ) : (
-
- {loading ? (
-
- ) : (
-
-
-
-
-
-
-
- {translations.RISKY}
-
- }
- />
-
- )}
-
)}
diff --git a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts
index f315908be2a71..65743d468ed2a 100644
--- a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts
+++ b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts
@@ -6,6 +6,9 @@
*/
import { useEffect } from 'react';
+import type { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils';
+import type { EntityRiskInput } from '../../../common/entity_analytics/risk_engine';
+
import { useQueryAlerts } from '../../detections/containers/detection_engine/alerts/use_query';
import { ALERTS_QUERY_NAMES } from '../../detections/containers/detection_engine/alerts/constants';
@@ -13,25 +16,33 @@ import type {
UserRiskScore,
HostRiskScore,
} from '../../../common/search_strategy/security_solution/risk_score/all';
-import { getAlertsQueryForRiskScore } from '../common/get_alerts_query_for_risk_score';
-
-import { useRiskEngineSettings } from '../api/hooks/use_risk_engine_settings';
+import { isUserRiskScore } from '../../../common/search_strategy/security_solution/risk_score/all';
interface UseRiskContributingAlerts {
riskScore: UserRiskScore | HostRiskScore | undefined;
- fields?: string[];
}
-interface Hit {
- fields: Record;
+interface AlertData {
+ [ALERT_RULE_UUID]: string;
+ [ALERT_RULE_NAME]: string;
+}
+
+interface AlertHit {
+ _id: string;
_index: string;
+ _source: AlertData;
+}
+
+export interface InputAlert {
+ alert: AlertData;
+ input: EntityRiskInput;
_id: string;
}
-interface UseRiskContributingAlertsResult {
+export interface UseRiskContributingAlertsResult {
loading: boolean;
error: boolean;
- data?: Hit[];
+ data?: InputAlert[];
}
/**
@@ -39,33 +50,48 @@ interface UseRiskContributingAlertsResult {
*/
export const useRiskContributingAlerts = ({
riskScore,
- fields,
}: UseRiskContributingAlerts): UseRiskContributingAlertsResult => {
- const { data: riskEngineSettings } = useRiskEngineSettings();
-
- const { loading, data, setQuery } = useQueryAlerts({
- // is empty query, to skip fetching alert, until we have risk engine settings
+ const { loading, data, setQuery } = useQueryAlerts({
query: {},
queryName: ALERTS_QUERY_NAMES.BY_ID,
});
- useEffect(() => {
- if (!riskEngineSettings?.range?.start || !riskScore) return;
+ const inputs = getInputs(riskScore);
- setQuery(
- getAlertsQueryForRiskScore({
- riskRangeStart: riskEngineSettings.range.start,
- riskScore,
- fields,
- })
- );
- }, [setQuery, riskScore, riskEngineSettings?.range?.start, fields]);
+ useEffect(() => {
+ if (!riskScore) return;
+ setQuery({
+ query: {
+ ids: {
+ values: inputs.map((input) => input.id),
+ },
+ },
+ });
+ }, [riskScore, inputs, setQuery]);
const error = !loading && data === undefined;
+ const alerts = inputs.map((input) => ({
+ _id: input.id,
+ input,
+ alert: (data?.hits.hits.find((alert) => alert._id === input.id)?._source || {}) as AlertData,
+ }));
+
return {
loading,
error,
- data: data?.hits.hits,
+ data: alerts,
};
};
+
+const getInputs = (riskScore?: UserRiskScore | HostRiskScore) => {
+ if (!riskScore) {
+ return [];
+ }
+
+ if (isUserRiskScore(riskScore)) {
+ return riskScore.user.risk.inputs;
+ }
+
+ return riskScore.host.risk.inputs;
+};
diff --git a/x-pack/plugins/security_solution/public/explore/components/authentication/helpers.tsx b/x-pack/plugins/security_solution/public/explore/components/authentication/helpers.tsx
index 9ca08cc8cd1e4..80b5924860ecb 100644
--- a/x-pack/plugins/security_solution/public/explore/components/authentication/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/authentication/helpers.tsx
@@ -19,11 +19,9 @@ import {
UserDetailsLink,
} from '../../../common/components/links';
import type { AuthenticationsEdges } from '../../../../common/search_strategy';
-import { MatrixHistogramType } from '../../../../common/search_strategy';
import type { AuthTableColumns } from './types';
import type {
MatrixHistogramConfigs,
- MatrixHistogramMappingTypes,
MatrixHistogramOption,
} from '../../../common/components/matrix_histogram/types';
import type { LensAttributes } from '../../../common/components/visualization_actions/types';
@@ -195,36 +193,10 @@ export const authenticationsStackByOptions: MatrixHistogramOption[] = [
];
const DEFAULT_STACK_BY = 'event.outcome';
-enum AuthenticationsMatrixDataGroup {
- authenticationsSuccess = 'success',
- authenticationsFailure = 'failure',
-}
-
-export enum ChartColors {
- authenticationsSuccess = '#54B399',
- authenticationsFailure = '#E7664C',
-}
-
-export const authenticationsMatrixDataMappingFields: MatrixHistogramMappingTypes = {
- [AuthenticationsMatrixDataGroup.authenticationsSuccess]: {
- key: AuthenticationsMatrixDataGroup.authenticationsSuccess,
- value: null,
- color: ChartColors.authenticationsSuccess,
- },
- [AuthenticationsMatrixDataGroup.authenticationsFailure]: {
- key: AuthenticationsMatrixDataGroup.authenticationsFailure,
- value: null,
- color: ChartColors.authenticationsFailure,
- },
-};
-
export const histogramConfigs: MatrixHistogramConfigs = {
defaultStackByOption:
authenticationsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ??
authenticationsStackByOptions[0],
- errorMessage: i18n.ERROR_FETCHING_AUTHENTICATIONS_DATA,
- histogramType: MatrixHistogramType.authentications,
- mapping: authenticationsMatrixDataMappingFields,
stackByOptions: authenticationsStackByOptions,
title: i18n.NAVIGATION_AUTHENTICATIONS_TITLE,
lensAttributes: authenticationLensAttributes as LensAttributes,
diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/common/index.ts b/x-pack/plugins/security_solution/public/explore/components/kpi/__mocks__/index.tsx
similarity index 72%
rename from x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/common/index.ts
rename to x-pack/plugins/security_solution/public/explore/components/kpi/__mocks__/index.tsx
index 4ee1044f5e4a3..e0a3ed3f05cae 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/matrix_histogram/common/index.ts
+++ b/x-pack/plugins/security_solution/public/explore/components/kpi/__mocks__/index.tsx
@@ -5,7 +5,6 @@
* 2.0.
*/
-export interface HistogramBucket {
- key: number;
- doc_count: number;
-}
+import React from 'react';
+
+export const KpiBaseComponent = jest.fn().mockReturnValue(
);
diff --git a/x-pack/plugins/security_solution/public/explore/components/kpi/index.tsx b/x-pack/plugins/security_solution/public/explore/components/kpi/index.tsx
new file mode 100644
index 0000000000000..de0794fdafef9
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/explore/components/kpi/index.tsx
@@ -0,0 +1,29 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiFlexGroup } from '@elastic/eui';
+
+import type { StatItems } from '../stat_items';
+import { StatItemsComponent } from '../stat_items';
+
+interface KpiBaseComponentProps {
+ from: string;
+ id: string;
+ statItems: Readonly;
+ to: string;
+}
+
+export const KpiBaseComponent = React.memo(({ statItems, ...props }) => (
+
+ {statItems.map((statItem) => (
+
+ ))}
+
+));
+
+KpiBaseComponent.displayName = 'KpiBaseComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/index.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/index.tsx
index 4b17b37c96f36..fafd3885914f3 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/index.tsx
@@ -7,4 +7,3 @@
export { StatItemsComponent } from './stat_items';
export type { StatItemsProps, StatItems } from './types';
-export { useKpiMatrixStatus } from './use_kpi_matrix_status';
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.test.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.test.tsx
deleted file mode 100644
index 30627023c91cf..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.test.tsx
+++ /dev/null
@@ -1,66 +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 { MetricProps } from './metric';
-import { Metric } from './metric';
-import type { RenderResult } from '@testing-library/react';
-import { render } from '@testing-library/react';
-import React from 'react';
-import { TestProviders } from '../../../common/mock';
-import type { LensAttributes } from '../../../common/components/visualization_actions/types';
-
-jest.mock('../../../common/components/visualization_actions/actions');
-
-describe('Metric', () => {
- const testProps = {
- fields: [
- {
- key: 'uniqueSourceIps',
- description: 'Source',
- value: 1714,
- color: '#D36086',
- icon: 'cross',
- lensAttributes: {} as LensAttributes,
- },
- {
- key: 'uniqueDestinationIps',
- description: 'Dest.',
- value: 2359,
- color: '#9170B8',
- icon: 'cross',
- lensAttributes: {} as LensAttributes,
- },
- ],
- id: 'test',
- timerange: { from: '', to: '' },
- isAreaChartDataAvailable: true,
- isBarChartDataAvailable: true,
- } as MetricProps;
-
- let res: RenderResult;
-
- beforeEach(() => {
- res = render(
-
-
-
- );
- });
-
- it('renders icons', () => {
- expect(res.getAllByTestId('stat-icon')).toHaveLength(2);
- });
-
- it('render titles', () => {
- expect(res.getAllByTestId('stat-title')[0]).toHaveTextContent('1,714 Source');
- expect(res.getAllByTestId('stat-title')[1]).toHaveTextContent('2,359 Dest.');
- });
-
- it('render actions', () => {
- expect(res.getAllByTestId('visualizationActions')).toHaveLength(2);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.tsx
deleted file mode 100644
index 1dd14b262108c..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric.tsx
+++ /dev/null
@@ -1,78 +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 { EuiFlexGroup, EuiIcon } from '@elastic/eui';
-import React from 'react';
-import type { StatItem } from './types';
-import { HoverVisibilityContainer } from '../../../common/components/hover_visibility_container';
-import { VisualizationActions } from '../../../common/components/visualization_actions/actions';
-import { FlexItem, StatValue } from './utils';
-import { getEmptyTagValue } from '../../../common/components/empty_value';
-import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../../../common/components/visualization_actions/utils';
-
-export interface MetricProps {
- fields: StatItem[];
- id: string;
- timerange: { from: string; to: string };
- isAreaChartDataAvailable: boolean;
- isBarChartDataAvailable: boolean;
- inspectTitle?: string;
- inspectIndex?: number;
-}
-
-const MetricComponent = ({
- fields,
- id,
- timerange,
- isAreaChartDataAvailable,
- isBarChartDataAvailable,
- inspectTitle,
- inspectIndex,
-}: MetricProps) => {
- return (
-
- {fields.map((field) => (
-
-
- {(isAreaChartDataAvailable || isBarChartDataAvailable) && field.icon && (
-
-
-
- )}
-
-
-
-
-
- {field.value != null ? field.value.toLocaleString() : getEmptyTagValue()}{' '}
- {field.description}
-
-
- {field.lensAttributes && timerange && (
-
- )}
-
-
-
-
- ))}
-
- );
-};
-
-export const Metric = React.memo(MetricComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric_embeddable.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/metric_embeddable.tsx
index ddc242bb13ddc..c6e44b4dda306 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/metric_embeddable.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/metric_embeddable.tsx
@@ -7,14 +7,14 @@
import { EuiFlexGroup, EuiIcon } from '@elastic/eui';
import React from 'react';
import { FlexItem, MetricItem, StatValue } from './utils';
-import type { MetricStatItem } from './types';
import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
+import type { FieldConfigs } from './types';
export interface MetricEmbeddableProps {
- fields: MetricStatItem[];
+ fields: FieldConfigs[];
id: string;
- timerange: { from: string; to: string };
inspectTitle?: string;
+ timerange: { from: string; to: string };
}
const CHART_HEIGHT = 36;
@@ -22,14 +22,9 @@ const CHART_HEIGHT = 36;
const MetricEmbeddableComponent = ({
fields,
id,
- timerange,
inspectTitle,
-}: {
- fields: MetricStatItem[];
- id: string;
- timerange: { from: string; to: string };
- inspectTitle?: string;
-}) => {
+ timerange,
+}: MetricEmbeddableProps) => {
return (
{fields.map((field) => (
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.test.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.test.tsx
index dbbdab338b623..ad549a5e02e72 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.test.tsx
@@ -5,245 +5,67 @@
* 2.0.
*/
-import type { ReactWrapper } from 'enzyme';
-import { mount } from 'enzyme';
import React from 'react';
-import { ThemeProvider } from 'styled-components';
-
-import type { StatItemsProps } from '.';
+import { render, fireEvent, waitFor } from '@testing-library/react';
import { StatItemsComponent } from './stat_items';
-import { BarChart } from '../../../common/components/charts/barchart';
-import { AreaChart } from '../../../common/components/charts/areachart';
-import { EuiHorizontalRule } from '@elastic/eui';
-import { mockUpdateDateRange } from '../../network/components/kpi_network/mock';
-import { createMockStore } from '../../../common/mock';
-import { Provider as ReduxStoreProvider } from 'react-redux';
-import { getMockTheme } from '../../../common/lib/kibana/kibana_react.mock';
-import * as module from '../../../common/containers/query_toggle';
import type { LensAttributes } from '../../../common/components/visualization_actions/types';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
-
-const from = '2019-06-15T06:00:00.000Z';
-const to = '2019-06-18T06:00:00.000Z';
-
-jest.mock('../../../common/components/charts/areachart', () => {
- return { AreaChart: () =>
};
-});
-
-jest.mock('../../../common/components/charts/barchart', () => {
- return { BarChart: () =>
};
-});
-
-jest.mock('../../../common/components/visualization_actions/actions');
-jest.mock('../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
+import { TestProviders } from '../../../common/mock/test_providers';
+import { useToggleStatus } from './use_toggle_status';
jest.mock('../../../common/components/visualization_actions/visualization_embeddable');
+jest.mock('./use_toggle_status', () => ({
+ useToggleStatus: jest.fn().mockReturnValue({ isToggleExpanded: true, onToggle: jest.fn() }),
+}));
-const mockSetToggle = jest.fn();
-const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock;
-
-jest
- .spyOn(module, 'useQueryToggle')
- .mockImplementation(() => ({ toggleStatus: true, setToggleStatus: mockSetToggle }));
-const mockSetQuerySkip = jest.fn();
-describe('Stat Items Component', () => {
- const mockTheme = getMockTheme({ eui: { euiColorMediumShade: '#ece' } });
- const store = createMockStore();
- const testProps = {
- description: 'HOSTS',
- fields: [{ key: 'hosts', value: null, color: '#6092C0', icon: 'cross' }],
- from,
- id: 'hostsKpiHostsQuery',
- key: 'mock-keys',
- loading: false,
- setQuerySkip: mockSetQuerySkip,
- to,
- updateDateRange: mockUpdateDateRange,
- };
- beforeEach(() => {
- jest.clearAllMocks();
- });
- describe.each([
- [
- mount(
-
-
-
-
-
- ),
- ],
- [
- mount(
-
-
-
-
-
- ),
- ],
- ])('disable charts', (wrapper) => {
- test('should render titles', () => {
- expect(wrapper.find('[data-test-subj="stat-title"]')).toBeTruthy();
- });
-
- test('should not render icons', () => {
- expect(wrapper.find('[data-test-subj="stat-icon"]').filter('EuiIcon')).toHaveLength(0);
- });
-
- test('should not render barChart', () => {
- expect(wrapper.find(BarChart)).toHaveLength(0);
- });
-
- test('should not render areaChart', () => {
- expect(wrapper.find(AreaChart)).toHaveLength(0);
- });
-
- test('should not render spliter', () => {
- expect(wrapper.find(EuiHorizontalRule)).toHaveLength(0);
- });
- });
-
- const mockStatItemsData: StatItemsProps = {
- ...testProps,
- id: 'UniqueIps',
- areaChart: [
- {
- key: 'uniqueSourceIpsHistogram',
- value: [
- { x: new Date('2019-05-03T13:00:00.000Z').toISOString(), y: 565975 },
- { x: new Date('2019-05-04T01:00:00.000Z').toISOString(), y: 1084366 },
- { x: new Date('2019-05-04T13:00:00.000Z').toISOString(), y: 12280 },
- ],
- color: '#D36086',
- },
- {
- key: 'uniqueDestinationIpsHistogram',
- value: [
- { x: new Date('2019-05-03T13:00:00.000Z').toISOString(), y: 565975 },
- { x: new Date('2019-05-04T01:00:00.000Z').toISOString(), y: 1084366 },
- { x: new Date('2019-05-04T13:00:00.000Z').toISOString(), y: 12280 },
- ],
- color: '#9170B8',
- },
- ],
- barChart: [
- { key: 'uniqueSourceIps', value: [{ x: 'uniqueSourceIps', y: '1714' }], color: '#D36086' },
- {
- key: 'uniqueDestinationIps',
- value: [{ x: 'uniqueDestinationIps', y: 2354 }],
- color: '#9170B8',
- },
- ],
- description: 'UNIQUE_PRIVATE_IPS',
- enableAreaChart: true,
- enableBarChart: true,
+describe('StatItemsComponent', () => {
+ const mockStatItems = {
+ key: 'hosts',
fields: [
{
- key: 'uniqueSourceIps',
- description: 'Source',
- value: 1714,
- color: '#D36086',
- icon: 'cross',
- lensAttributes: {} as LensAttributes,
- },
- {
- key: 'uniqueDestinationIps',
- description: 'Dest.',
- value: 2359,
- color: '#9170B8',
- icon: 'cross',
+ key: 'hosts',
+ value: null,
+ color: '#fff',
+ icon: 'storage',
lensAttributes: {} as LensAttributes,
},
],
- barChartLensAttributes: {} as LensAttributes,
+ enableAreaChart: true,
+ description: 'Mock Description',
areaChartLensAttributes: {} as LensAttributes,
};
- let wrapper: ReactWrapper;
- describe('rendering kpis with charts', () => {
- beforeAll(() => {
- wrapper = mount(
-
-
-
- );
- });
-
- test('should handle multiple titles', () => {
- expect(wrapper.find('[data-test-subj="stat-title"]').find('p')).toHaveLength(2);
- });
-
- test('should render kpi icons', () => {
- expect(wrapper.find('[data-test-subj="stat-icon"]').filter('EuiIcon')).toHaveLength(2);
- });
+ const mockProps = {
+ statItems: mockStatItems,
+ from: new Date('2023-01-01').toISOString(),
+ to: new Date('2023-12-31').toISOString(),
+ id: 'mockId',
+ };
- test('should render barChart', () => {
- expect(wrapper.find(BarChart)).toHaveLength(1);
+ it('renders visualizations', () => {
+ const { getByText, getAllByTestId } = render( , {
+ wrapper: TestProviders,
});
- test('should render areaChart', () => {
- expect(wrapper.find(AreaChart)).toHaveLength(1);
- });
+ expect(getByText('Mock Description')).toBeInTheDocument();
- test('should render separator', () => {
- expect(wrapper.find(EuiHorizontalRule)).toHaveLength(1);
- });
+ expect(getAllByTestId('visualization-embeddable')).toHaveLength(2);
});
- describe('Toggle query', () => {
- test('toggleQuery updates toggleStatus', () => {
- wrapper = mount(
-
-
-
- );
- wrapper.find('[data-test-subj="query-toggle-stat"]').first().simulate('click');
- expect(mockSetToggle).toBeCalledWith(false);
- expect(mockSetQuerySkip).toBeCalledWith(true);
- });
- test('toggleStatus=true, render all', () => {
- wrapper = mount(
-
-
-
- );
- expect(wrapper.find(`.viz-actions`).exists()).toEqual(true);
- expect(wrapper.find('[data-test-subj="stat-title"]').first().exists()).toEqual(true);
+ it('toggles visualizations', () => {
+ (useToggleStatus as jest.Mock).mockReturnValue({
+ isToggleExpanded: false,
+ onToggle: jest.fn(),
});
- test('toggleStatus=false, render none', () => {
- jest
- .spyOn(module, 'useQueryToggle')
- .mockImplementation(() => ({ toggleStatus: false, setToggleStatus: mockSetToggle }));
- wrapper = mount(
-
-
-
- );
- expect(wrapper.find('.viz-actions').first().exists()).toEqual(false);
- expect(wrapper.find('[data-test-subj="stat-title"]').first().exists()).toEqual(false);
+ const { getByTestId, getAllByTestId } = render( , {
+ wrapper: TestProviders,
});
- });
-
- describe('when isChartEmbeddablesEnabled = true', () => {
- beforeAll(() => {
- mockUseIsExperimentalFeatureEnabled.mockReturnValue(true);
- jest
- .spyOn(module, 'useQueryToggle')
- .mockImplementation(() => ({ toggleStatus: true, setToggleStatus: mockSetToggle }));
- wrapper = mount(
-
-
-
- );
- });
+ const toggleButton = getByTestId('query-toggle-stat');
+ fireEvent.click(toggleButton);
- test('renders Lens Embeddables', () => {
- expect(wrapper.find('[data-test-subj="visualization-embeddable"]').length).toEqual(4);
+ waitFor(() => {
+ expect(getAllByTestId('visualization-embeddable')).toHaveLength(0);
});
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.tsx
index 1de63dff04903..570ffadca64e7 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/stat_items.tsx
@@ -5,191 +5,89 @@
* 2.0.
*/
-import {
- EuiFlexGroup,
- EuiPanel,
- EuiHorizontalRule,
- EuiFlexItem,
- EuiLoadingSpinner,
-} from '@elastic/eui';
+import { EuiFlexGroup, EuiPanel, EuiHorizontalRule } from '@elastic/eui';
import React, { useMemo } from 'react';
-import deepEqual from 'fast-deep-equal';
-
-import { AreaChart } from '../../../common/components/charts/areachart';
-import { BarChart } from '../../../common/components/charts/barchart';
-
-import { histogramDateTimeFormatter } from '../../../common/components/utils';
import { StatItemHeader } from './stat_item_header';
import { useToggleStatus } from './use_toggle_status';
import type { StatItemsProps } from './types';
-import { areachartConfigs, barchartConfigs, FlexItem, ChartHeight } from './utils';
-import { Metric } from './metric';
+import { FlexItem, ChartHeight } from './utils';
import { MetricEmbeddable } from './metric_embeddable';
-import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features';
import { VisualizationEmbeddable } from '../../../common/components/visualization_actions/visualization_embeddable';
-export const StatItemsComponent = React.memo(
- ({
- areaChart,
- barChart,
+export const StatItemsComponent = React.memo(({ statItems, from, id, to }) => {
+ const timerange = useMemo(
+ () => ({
+ from,
+ to,
+ }),
+ [from, to]
+ );
+ const {
+ key,
description,
enableAreaChart,
enableBarChart,
fields,
- from,
- grow,
- id,
- loading = false,
- index,
- updateDateRange,
- statKey = 'item',
- to,
barChartLensAttributes,
areaChartLensAttributes,
- setQuerySkip,
- }) => {
- const isBarChartDataAvailable = !!(
- barChart &&
- barChart.length &&
- barChart.every((item) => item.value != null && item.value.length > 0)
- );
- const isAreaChartDataAvailable = !!(
- areaChart &&
- areaChart.length &&
- areaChart.every((item) => item.value != null && item.value.length > 0)
- );
+ } = statItems;
- const timerange = useMemo(
- () => ({
- from,
- to,
- }),
- [from, to]
- );
+ const { isToggleExpanded, onToggle } = useToggleStatus({ id });
- const { isToggleExpanded, onToggle } = useToggleStatus({ id, setQuerySkip });
+ return (
+
+
+
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
+ {isToggleExpanded && (
+ <>
+
- return (
-
-
-
- {loading && (
-
-
-
-
-
- )}
- {isToggleExpanded && !loading && (
- <>
- {isChartEmbeddablesEnabled ? (
-
- ) : (
-
+ {(enableAreaChart || enableBarChart) && }
+
+ {enableBarChart && (
+
+
+
)}
- {(enableAreaChart || enableBarChart) && }
-
- {enableBarChart && (
+
+ {enableAreaChart && from != null && to != null && (
+ <>
- {isChartEmbeddablesEnabled && barChartLensAttributes ? (
-
- ) : (
-
- )}
+
- )}
-
- {enableAreaChart && from != null && to != null && (
- <>
-
- {isChartEmbeddablesEnabled && areaChartLensAttributes ? (
-
- ) : (
-
- )}
-
- >
- )}
-
- >
- )}
-
-
- );
- },
- (prevProps, nextProps) =>
- prevProps.description === nextProps.description &&
- prevProps.enableAreaChart === nextProps.enableAreaChart &&
- prevProps.enableBarChart === nextProps.enableBarChart &&
- prevProps.from === nextProps.from &&
- prevProps.grow === nextProps.grow &&
- prevProps.loading === nextProps.loading &&
- prevProps.setQuerySkip === nextProps.setQuerySkip &&
- prevProps.id === nextProps.id &&
- prevProps.index === nextProps.index &&
- prevProps.updateDateRange === nextProps.updateDateRange &&
- prevProps.statKey === nextProps.statKey &&
- prevProps.to === nextProps.to &&
- deepEqual(prevProps.areaChart, nextProps.areaChart) &&
- deepEqual(prevProps.barChart, nextProps.barChart) &&
- deepEqual(prevProps.fields, nextProps.fields)
-);
+ >
+ )}
+
+ >
+ )}
+
+
+ );
+});
StatItemsComponent.displayName = 'StatItemsComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/types.ts b/x-pack/plugins/security_solution/public/explore/components/stat_items/types.ts
index 07003fd462e82..bece997a63aad 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/types.ts
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/types.ts
@@ -5,54 +5,31 @@
* 2.0.
*/
import type { IconType } from '@elastic/eui';
-import type {
- ChartSeriesConfigs,
- ChartSeriesData,
- UpdateDateRange,
-} from '../../../common/components/charts/common';
+
import type { LensAttributes } from '../../../common/components/visualization_actions/types';
-export interface MetricStatItem {
+export interface FieldConfigs {
color?: string;
description?: string;
icon?: IconType;
key: string;
- name?: string;
lensAttributes?: LensAttributes;
-}
-
-export interface StatItem {
- color?: string;
- description?: string;
- icon?: IconType;
- key: string;
name?: string;
- value: number | undefined | null;
- lensAttributes?: LensAttributes;
}
export interface StatItems {
- areachartConfigs?: ChartSeriesConfigs;
- barchartConfigs?: ChartSeriesConfigs;
description?: string;
enableAreaChart?: boolean;
enableBarChart?: boolean;
- fields: StatItem[];
- grow?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | true | false | null;
- index?: number;
+ fields: FieldConfigs[];
key: string;
- statKey?: string;
barChartLensAttributes?: LensAttributes;
areaChartLensAttributes?: LensAttributes;
}
-export interface StatItemsProps extends StatItems {
- areaChart?: ChartSeriesData[];
- barChart?: ChartSeriesData[];
+export interface StatItemsProps {
from: string;
id: string;
- updateDateRange: UpdateDateRange;
+ statItems: StatItems;
to: string;
- loading: boolean;
- setQuerySkip: (skip: boolean) => void;
}
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.test.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.test.tsx
deleted file mode 100644
index 0cd675bdd6416..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.test.tsx
+++ /dev/null
@@ -1,90 +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 { mount } from 'enzyme';
-import React from 'react';
-import type { StatItemsProps, StatItems } from '.';
-
-import { fieldsMapping as fieldTitleChartMapping } from '../../network/components/kpi_network/unique_private_ips';
-import {
- mockData,
- mockEnableChartsData,
- mockNoChartMappings,
- mockUpdateDateRange,
-} from '../../network/components/kpi_network/mock';
-
-import type {
- HostsKpiStrategyResponse,
- NetworkKpiStrategyResponse,
-} from '../../../../common/search_strategy';
-import { useKpiMatrixStatus } from './use_kpi_matrix_status';
-const mockSetQuerySkip = jest.fn();
-const from = '2019-06-15T06:00:00.000Z';
-const to = '2019-06-18T06:00:00.000Z';
-
-describe('useKpiMatrixStatus', () => {
- const mockNetworkMappings = fieldTitleChartMapping;
- const MockChildComponent = (mappedStatItemProps: StatItemsProps) => ;
- const MockHookWrapperComponent = ({
- fieldsMapping,
- data,
- }: {
- fieldsMapping: Readonly;
- data: NetworkKpiStrategyResponse | HostsKpiStrategyResponse;
- }) => {
- const statItemsProps: StatItemsProps[] = useKpiMatrixStatus(
- fieldsMapping,
- data,
- 'statItem',
- from,
- to,
- mockUpdateDateRange,
- mockSetQuerySkip,
- false
- );
-
- return (
-
- {statItemsProps.map((mappedStatItemProps) => {
- return ;
- })}
-
- );
- };
-
- test('it updates status correctly', () => {
- const wrapper = mount(
- <>
-
- >
- );
- const result = { ...wrapper.find('MockChildComponent').get(0).props };
- const { setQuerySkip, ...restResult } = result;
- const { setQuerySkip: a, ...restExpect } = mockEnableChartsData;
- expect(restResult).toEqual(restExpect);
- });
-
- test('it should not append areaChart if enableAreaChart is off', () => {
- const wrapper = mount(
- <>
-
- >
- );
-
- expect(wrapper.find('MockChildComponent').get(0).props.areaChart).toBeUndefined();
- });
-
- test('it should not append barChart if enableBarChart is off', () => {
- const wrapper = mount(
- <>
-
- >
- );
-
- expect(wrapper.find('MockChildComponent').get(0).props.barChart).toBeUndefined();
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.ts b/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.ts
deleted file mode 100644
index 9ddeed04b0786..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_kpi_matrix_status.ts
+++ /dev/null
@@ -1,39 +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 {
- HostsKpiStrategyResponse,
- NetworkKpiStrategyResponse,
- UsersKpiStrategyResponse,
-} from '../../../../common/search_strategy';
-import type { UpdateDateRange } from '../../../common/components/charts/common';
-import type { StatItems, StatItemsProps } from './types';
-import { addValueToAreaChart, addValueToBarChart, addValueToFields } from './utils';
-
-export const useKpiMatrixStatus = (
- mappings: Readonly,
- data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse | UsersKpiStrategyResponse,
- id: string,
- from: string,
- to: string,
- updateDateRange: UpdateDateRange,
- setQuerySkip: (skip: boolean) => void,
- loading: boolean
-): StatItemsProps[] =>
- mappings.map((stat) => ({
- ...stat,
- areaChart: stat.enableAreaChart ? addValueToAreaChart(stat.fields, data) : undefined,
- barChart: stat.enableBarChart ? addValueToBarChart(stat.fields, data) : undefined,
- fields: addValueToFields(stat.fields, data),
- id,
- key: `kpi-summary-${stat.key}`,
- statKey: `${stat.key}`,
- from,
- to,
- updateDateRange,
- setQuerySkip,
- loading,
- }));
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_toggle_status.ts b/x-pack/plugins/security_solution/public/explore/components/stat_items/use_toggle_status.ts
index 900a97d99c09e..224229f94939e 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/use_toggle_status.ts
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/use_toggle_status.ts
@@ -7,24 +7,13 @@
import { useCallback } from 'react';
import { useQueryToggle } from '../../../common/containers/query_toggle';
-export const useToggleStatus = ({
- id,
- setQuerySkip,
-}: {
- id: string;
- setQuerySkip: (skip: boolean) => void;
-}) => {
+export const useToggleStatus = ({ id }: { id: string }) => {
const { toggleStatus, setToggleStatus } = useQueryToggle(id);
- const toggleQuery = useCallback(
- (status: boolean) => {
- setToggleStatus(status);
- // toggleStatus on = skipQuery false
- setQuerySkip(!status);
- },
- [setQuerySkip, setToggleStatus]
+ const onToggle = useCallback(
+ () => setToggleStatus(!toggleStatus),
+ [setToggleStatus, toggleStatus]
);
- const onToggle = useCallback(() => toggleQuery(!toggleStatus), [toggleQuery, toggleStatus]);
return {
isToggleExpanded: toggleStatus,
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.test.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.test.tsx
deleted file mode 100644
index afab433b2bf66..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.test.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { addValueToFields, addValueToAreaChart, addValueToBarChart } from './utils';
-import { fieldsMapping as fieldTitleChartMapping } from '../../network/components/kpi_network/unique_private_ips';
-
-import { mockData, mockEnableChartsData } from '../../network/components/kpi_network/mock';
-
-describe('addValueToFields', () => {
- const mockNetworkMappings = fieldTitleChartMapping[0];
- test('should update value from data', () => {
- const result = addValueToFields(mockNetworkMappings.fields, mockData);
- expect(result).toEqual(mockEnableChartsData.fields);
- });
-});
-
-describe('addValueToAreaChart', () => {
- const mockNetworkMappings = fieldTitleChartMapping[0];
- test('should add areaChart from data', () => {
- const result = addValueToAreaChart(mockNetworkMappings.fields, mockData);
- expect(result).toEqual(mockEnableChartsData.areaChart);
- });
-});
-
-describe('addValueToBarChart', () => {
- const mockNetworkMappings = fieldTitleChartMapping[0];
- test('should add areaChart from data', () => {
- const result = addValueToBarChart(mockNetworkMappings.fields, mockData);
- expect(result).toEqual(mockEnableChartsData.barChart);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.tsx b/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.tsx
index c345c46935000..f0393cf5cc71c 100644
--- a/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.tsx
+++ b/x-pack/plugins/security_solution/public/explore/components/stat_items/utils.tsx
@@ -5,20 +5,9 @@
* 2.0.
*/
-import type { BrushEndListener, ElementClickListener, Rotation } from '@elastic/charts';
-import { ScaleType } from '@elastic/charts';
import styled from 'styled-components';
-import { get, getOr } from 'lodash/fp';
import { EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
-import type {
- HostsKpiStrategyResponse,
- NetworkKpiStrategyResponse,
- UsersKpiStrategyResponse,
-} from '../../../../common/search_strategy';
-import type { ChartSeriesData, ChartData } from '../../../common/components/charts/common';
-
-import type { StatItem } from './types';
export const ChartHeight = 120;
@@ -54,87 +43,3 @@ StatValue.displayName = 'StatValue';
export const StyledTitle = styled.h6`
line-height: 200%;
`;
-
-export const numberFormatter = (value: string | number): string => value.toLocaleString();
-export const statItemBarchartRotation: Rotation = 90;
-export const statItemChartCustomHeight = 74;
-
-export const areachartConfigs = (config?: {
- xTickFormatter: (value: number) => string;
- onBrushEnd?: BrushEndListener;
-}) => ({
- series: {
- xScaleType: ScaleType.Time,
- yScaleType: ScaleType.Linear,
- },
- axis: {
- xTickFormatter: get('xTickFormatter', config),
- yTickFormatter: numberFormatter,
- },
- settings: {
- onBrushEnd: getOr(() => {}, 'onBrushEnd', config),
- },
- customHeight: statItemChartCustomHeight,
-});
-
-export const barchartConfigs = (config?: { onElementClick?: ElementClickListener }) => ({
- series: {
- xScaleType: ScaleType.Ordinal,
- yScaleType: ScaleType.Linear,
- stackAccessors: ['y0'],
- },
- axis: {
- xTickFormatter: numberFormatter,
- },
- settings: {
- onElementClick: getOr(() => {}, 'onElementClick', config),
- rotation: statItemBarchartRotation,
- },
- customHeight: statItemChartCustomHeight,
-});
-
-export const addValueToFields = (
- fields: StatItem[],
- data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse | UsersKpiStrategyResponse
-): StatItem[] => fields.map((field) => ({ ...field, value: get(field.key, data) }));
-
-export const addValueToAreaChart = (
- fields: StatItem[],
- data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse | UsersKpiStrategyResponse
-): ChartSeriesData[] =>
- fields
- .filter((field) => get(`${field.key}Histogram`, data) != null)
- .map(({ lensAttributes, ...field }) => ({
- ...field,
- value: get(`${field.key}Histogram`, data),
- key: `${field.key}Histogram`,
- }));
-
-export const addValueToBarChart = (
- fields: StatItem[],
- data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse | UsersKpiStrategyResponse
-): ChartSeriesData[] => {
- if (fields.length === 0) return [];
- return fields.reduce((acc: ChartSeriesData[], field: StatItem, idx: number) => {
- const { key, color } = field;
- const y: number | null = getOr(null, key, data);
- const x: string = get(`${idx}.name`, fields) || getOr('', `${idx}.description`, fields);
- const value: [ChartData] = [
- {
- x,
- y,
- g: key,
- y0: 0,
- },
- ];
-
- return [
- ...acc,
- {
- key,
- color,
- value,
- },
- ];
- }, []);
-};
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/common/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/common/index.tsx
deleted file mode 100644
index 7413563e0c950..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/common/index.tsx
+++ /dev/null
@@ -1,75 +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 { EuiFlexGroup } from '@elastic/eui';
-import styled from 'styled-components';
-import deepEqual from 'fast-deep-equal';
-
-import { manageQuery } from '../../../../../common/components/page/manage_query';
-import type {
- HostsKpiStrategyResponse,
- NetworkKpiStrategyResponse,
-} from '../../../../../../common/search_strategy';
-import type { StatItemsProps, StatItems } from '../../../../components/stat_items';
-import { StatItemsComponent, useKpiMatrixStatus } from '../../../../components/stat_items';
-import type { UpdateDateRange } from '../../../../../common/components/charts/common';
-import type { UsersKpiStrategyResponse } from '../../../../../../common/search_strategy/security_solution/users';
-
-const kpiWidgetHeight = 247;
-
-export const FlexGroup = styled(EuiFlexGroup)`
- min-height: ${kpiWidgetHeight}px;
-`;
-
-FlexGroup.displayName = 'FlexGroup';
-
-interface KpiBaseComponentProps {
- fieldsMapping: Readonly;
- data: HostsKpiStrategyResponse | NetworkKpiStrategyResponse | UsersKpiStrategyResponse;
- loading?: boolean;
- id: string;
- from: string;
- to: string;
- updateDateRange: UpdateDateRange;
- setQuerySkip: (skip: boolean) => void;
-}
-
-export const KpiBaseComponent = React.memo(
- ({ fieldsMapping, data, id, loading = false, from, to, updateDateRange, setQuerySkip }) => {
- const statItemsProps: StatItemsProps[] = useKpiMatrixStatus(
- fieldsMapping,
- data,
- id,
- from,
- to,
- updateDateRange,
- setQuerySkip,
- loading
- );
-
- return (
-
- {statItemsProps.map((mappedStatItemProps) => (
-
- ))}
-
- );
- },
- (prevProps, nextProps) =>
- prevProps.fieldsMapping === nextProps.fieldsMapping &&
- prevProps.id === nextProps.id &&
- prevProps.loading === nextProps.loading &&
- prevProps.from === nextProps.from &&
- prevProps.to === nextProps.to &&
- prevProps.updateDateRange === nextProps.updateDateRange &&
- deepEqual(prevProps.data, nextProps.data)
-);
-
-KpiBaseComponent.displayName = 'KpiBaseComponent';
-
-export const KpiBaseComponentManage = manageQuery(KpiBaseComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.test.tsx
index fb592f3910fb6..6044ee0de6d54 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useHostsKpiHosts } from '../../../containers/kpi_hosts/hosts';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { HostsKpiHosts } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../common';
+import { hostsStatItems, HostsKpiHosts } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_hosts/hosts');
-jest.mock('../common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Hosts KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('KPI Hosts', () => {
- const mockUseHostsKpiHosts = useHostsKpiHosts as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockRefetch = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseHostsKpiHosts.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseHostsKpiHosts.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseHostsKpiHosts.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(hostsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.tsx
index 476aedd473586..04ceab7438059 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/hosts/index.tsx
@@ -5,28 +5,24 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiHostAreaLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_host_area';
import { kpiHostMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_host_metric';
-import { useHostsKpiHosts, ID } from '../../../containers/kpi_hosts/hosts';
-import { KpiBaseComponentManage } from '../common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import type { HostsKpiProps } from '../types';
import { HostsKpiChartColors } from '../types';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-export const fieldsMapping: Readonly = [
+export const ID = 'hostsKpiHostsQuery';
+
+export const hostsStatItems: Readonly = [
{
key: 'hosts',
fields: [
{
key: 'hosts',
- value: null,
color: HostsKpiChartColors.hosts,
icon: 'storage',
lensAttributes: kpiHostMetricLensAttributes,
@@ -38,52 +34,8 @@ export const fieldsMapping: Readonly = [
},
];
-const HostsKpiHostsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useHostsKpiHosts({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const HostsKpiHostsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const HostsKpiHosts = React.memo(HostsKpiHostsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/index.tsx
index 8c315de005691..556c71421b896 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/index.tsx
@@ -12,55 +12,15 @@ import { HostsKpiHosts } from './hosts';
import { HostsKpiUniqueIps } from './unique_ips';
import type { HostsKpiProps } from './types';
-export const HostsKpiComponent = React.memo(
- ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => (
-
-
-
-
-
-
-
-
- )
-);
+export const HostsKpiComponent = React.memo(({ from, to }) => (
+
+
+
+
+
+
+
+
+));
HostsKpiComponent.displayName = 'HostsKpiComponent';
-
-export const HostsDetailsKpiComponent = React.memo(
- ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => {
- return (
-
-
-
-
-
- );
- }
-);
-
-HostsDetailsKpiComponent.displayName = 'HostsDetailsKpiComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/types.ts b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/types.ts
index 40e638bd645ce..b58367832f850 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/types.ts
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/types.ts
@@ -5,17 +5,9 @@
* 2.0.
*/
-import type { UpdateDateRange } from '../../../../common/components/charts/common';
-import type { GlobalTimeArgs } from '../../../../common/containers/use_global_time';
-
export interface HostsKpiProps {
- filterQuery?: string;
from: string;
to: string;
- indexNames: string[];
- updateDateRange: UpdateDateRange;
- setQuery: GlobalTimeArgs['setQuery'];
- skip: boolean;
}
export enum HostsKpiChartColors {
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.test.tsx
index 67d9aa0366c4f..2fbb35328286d 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useHostsKpiUniqueIps } from '../../../containers/kpi_hosts/unique_ips';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { HostsKpiUniqueIps } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../common';
+import { uniqueIpsStatItems, HostsKpiUniqueIps } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_hosts/unique_ips');
-jest.mock('../common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Hosts KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('KPI Unique IPs', () => {
- const mockUseHostsKpiUniqueIps = useHostsKpiUniqueIps as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseHostsKpiUniqueIps.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseHostsKpiUniqueIps.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseHostsKpiUniqueIps.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(uniqueIpsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.tsx
index a5bf77d5d1f94..41e7cb5c12413 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/components/kpi_hosts/unique_ips/index.tsx
@@ -5,24 +5,21 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiUniqueIpsAreaLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_area';
import { kpiUniqueIpsBarLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_bar';
import { kpiUniqueIpsDestinationMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_destination_metric';
import { kpiUniqueIpsSourceMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/hosts/kpi_unique_ips_source_metric';
-import { useHostsKpiUniqueIps, ID } from '../../../containers/kpi_hosts/unique_ips';
-import { KpiBaseComponentManage } from '../common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import type { HostsKpiProps } from '../types';
import { HostsKpiChartColors } from '../types';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-export const fieldsMapping: Readonly = [
+export const ID = 'hostsKpiUniqueIpsQuery';
+
+export const uniqueIpsStatItems: Readonly = [
{
key: 'uniqueIps',
fields: [
@@ -30,7 +27,6 @@ export const fieldsMapping: Readonly = [
key: 'uniqueSourceIps',
name: i18n.SOURCE_CHART_LABEL,
description: i18n.SOURCE_UNIT_LABEL,
- value: null,
color: HostsKpiChartColors.uniqueSourceIps,
icon: 'visMapCoordinate',
lensAttributes: kpiUniqueIpsSourceMetricLensAttributes,
@@ -39,7 +35,6 @@ export const fieldsMapping: Readonly = [
key: 'uniqueDestinationIps',
name: i18n.DESTINATION_CHART_LABEL,
description: i18n.DESTINATION_UNIT_LABEL,
- value: null,
color: HostsKpiChartColors.uniqueDestinationIps,
icon: 'visMapCoordinate',
lensAttributes: kpiUniqueIpsDestinationMetricLensAttributes,
@@ -53,52 +48,8 @@ export const fieldsMapping: Readonly = [
},
];
-const HostsKpiUniqueIpsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useHostsKpiUniqueIps({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const HostsKpiUniqueIpsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const HostsKpiUniqueIps = React.memo(HostsKpiUniqueIpsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.test.tsx
deleted file mode 100644
index 04ddb28a8c61c..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useHostsKpiHosts } from '.';
-
-describe('kpi hosts - hosts', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useHostsKpiHosts(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.tsx
deleted file mode 100644
index d84fa8665b7ab..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/index.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import type { KpiHostsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { HostsKpiHostsStrategyResponse } from '../../../../../../common/search_strategy';
-import { HostsKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'hostsKpiHostsQuery';
-
-export interface HostsKpiHostsArgs extends Omit {
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseHostsKpiHosts {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useHostsKpiHosts = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseHostsKpiHosts): [boolean, HostsKpiHostsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [hostsKpiHostsRequest, setHostsKpiHostsRequest] =
- useState(null);
-
- const [hostsKpiHostsResponse, setHostsKpiHostsResponse] = useState({
- hosts: 0,
- hostsHistogram: [],
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError, addWarning } = useAppToasts();
-
- const hostsKpiHostsSearch = useCallback(
- (request: KpiHostsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!response.isPartial && !response.isRunning) {
- setLoading(false);
- setHostsKpiHostsResponse((prevResponse) => ({
- ...prevResponse,
- hosts: response.hosts,
- hostsHistogram: response.hostsHistogram,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- } else if (response.isPartial && !response.isRunning) {
- setLoading(false);
- addWarning(i18n.ERROR_HOSTS_KPI_HOSTS);
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_HOSTS_KPI_HOSTS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, addWarning, skip]
- );
-
- useEffect(() => {
- setHostsKpiHostsRequest((prevRequest) => {
- const myRequest: KpiHostsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: HostsKpiQueries.kpiHosts,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- hostsKpiHostsSearch(hostsKpiHostsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [hostsKpiHostsRequest, hostsKpiHostsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, hostsKpiHostsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/translations.ts b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/translations.ts
deleted file mode 100644
index 6ce00635da881..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/hosts/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_HOSTS_KPI_HOSTS = i18n.translate(
- 'xpack.securitySolution.hostsKpiHosts.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on hosts kpi hosts search`,
- }
-);
-
-export const FAIL_HOSTS_KPI_HOSTS = i18n.translate(
- 'xpack.securitySolution.hostsKpiHosts.failSearchDescription',
- {
- defaultMessage: `Failed to run search on hosts kpi hosts`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.test.tsx b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.test.tsx
deleted file mode 100644
index 0050bde419807..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useHostsKpiUniqueIps } from '.';
-
-describe('kpi hosts - Unique Ips', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useHostsKpiUniqueIps(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.tsx
deleted file mode 100644
index 740282030286c..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/index.tsx
+++ /dev/null
@@ -1,165 +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 deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import type { KpiUniqueIpsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { HostsKpiUniqueIpsStrategyResponse } from '../../../../../../common/search_strategy';
-import { HostsKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'hostsKpiUniqueIpsQuery';
-
-export interface HostsKpiUniqueIpsArgs
- extends Omit {
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseHostsKpiUniqueIps {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useHostsKpiUniqueIps = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseHostsKpiUniqueIps): [boolean, HostsKpiUniqueIpsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
-
- const [hostsKpiUniqueIpsRequest, setHostsKpiUniqueIpsRequest] =
- useState(null);
-
- const [hostsKpiUniqueIpsResponse, setHostsKpiUniqueIpsResponse] = useState(
- {
- uniqueSourceIps: 0,
- uniqueSourceIpsHistogram: [],
- uniqueDestinationIps: 0,
- uniqueDestinationIpsHistogram: [],
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- }
- );
- const { addError, addWarning } = useAppToasts();
-
- const hostsKpiUniqueIpsSearch = useCallback(
- (request: KpiUniqueIpsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
- searchSubscription$.current = data.search
- .search(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!response.isPartial && !response.isRunning) {
- setLoading(false);
- setHostsKpiUniqueIpsResponse((prevResponse) => ({
- ...prevResponse,
- uniqueSourceIps: response.uniqueSourceIps,
- uniqueSourceIpsHistogram: response.uniqueSourceIpsHistogram,
- uniqueDestinationIps: response.uniqueDestinationIps,
- uniqueDestinationIpsHistogram: response.uniqueDestinationIpsHistogram,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- } else if (response.isPartial && !response.isRunning) {
- setLoading(false);
- addWarning(i18n.ERROR_HOSTS_KPI_UNIQUE_IPS);
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_HOSTS_KPI_UNIQUE_IPS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, addWarning, skip]
- );
-
- useEffect(() => {
- setHostsKpiUniqueIpsRequest((prevRequest) => {
- const myRequest: KpiUniqueIpsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: HostsKpiQueries.kpiUniqueIps,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, skip, startDate]);
-
- useEffect(() => {
- hostsKpiUniqueIpsSearch(hostsKpiUniqueIpsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [hostsKpiUniqueIpsRequest, hostsKpiUniqueIpsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, hostsKpiUniqueIpsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/translations.ts b/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/translations.ts
deleted file mode 100644
index 1d80d6dd131e9..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/hosts/containers/kpi_hosts/unique_ips/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_HOSTS_KPI_UNIQUE_IPS = i18n.translate(
- 'xpack.securitySolution.hostsKpiUniqueIps.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on hosts kpi unique ips search`,
- }
-);
-
-export const FAIL_HOSTS_KPI_UNIQUE_IPS = i18n.translate(
- 'xpack.securitySolution.hostsKpiUniqueIps.failSearchDescription',
- {
- defaultMessage: `Failed to run search on hosts kpi unique ips`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx
index 850a814f1503f..81165ddc9a545 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/hosts.tsx
@@ -9,7 +9,6 @@ import { EuiSpacer, EuiWindowEvent } from '@elastic/eui';
import styled from 'styled-components';
import { noop } from 'lodash/fp';
import React, { useCallback, useMemo, useRef } from 'react';
-import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
import type { Filter } from '@kbn/es-query';
import { isTab } from '@kbn/timelines-plugin/public';
@@ -17,7 +16,6 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common';
import { dataTableSelectors, tableDefaults, TableId } from '@kbn/securitysolution-data-table';
import { InputsModelId } from '../../../common/store/inputs/constants';
import { SecurityPageName } from '../../../app/types';
-import type { UpdateDateRange } from '../../../common/components/charts/common';
import { FiltersGlobal } from '../../../common/components/filters_global';
import { HeaderPage } from '../../../common/components/header_page';
import { LastEventTime } from '../../../common/components/last_event_time';
@@ -33,7 +31,6 @@ import { useKibana } from '../../../common/lib/kibana';
import { convertToBuildEsQuery } from '../../../common/lib/kuery';
import type { State } from '../../../common/store';
import { inputsSelectors } from '../../../common/store';
-import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions';
import { SpyRoute } from '../../../common/utils/route/spy_routes';
import { useMlCapabilities } from '../../../common/components/ml/hooks/use_ml_capabilities';
@@ -68,7 +65,6 @@ const StyledFullHeightContainer = styled.div`
`;
const HostsComponent = () => {
- const dispatch = useDispatch();
const containerElement = useRef(null);
const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []);
const graphEventId = useShallowEqualSelector(
@@ -109,22 +105,6 @@ const HostsComponent = () => {
return globalFilters;
}, [globalFilters, severitySelection, tabName]);
- const updateDateRange = useCallback(
- ({ x }) => {
- if (!x) {
- return;
- }
- const [min, max] = x;
- dispatch(
- setAbsoluteRangeDatePicker({
- id: InputsModelId.global,
- from: new Date(min).toISOString(),
- to: new Date(max).toISOString(),
- })
- );
- },
- [dispatch]
- );
const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } =
useSourcererDataView();
const [globalFilterQuery, kqlError] = useMemo(
@@ -203,15 +183,7 @@ const HostsComponent = () => {
border
/>
-
+
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/authentications_query_tab_body.tsx
index 2df170cd65b15..8a3ff93ceb245 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/authentications_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/authentications_query_tab_body.tsx
@@ -29,7 +29,6 @@ const AuthenticationsQueryTabBodyComponent: React.FC
endDate={endDate}
filterQuery={filterQuery}
id={HISTOGRAM_QUERY_ID}
- indexNames={indexNames}
setQuery={setQuery}
startDate={startDate}
{...histogramConfigs}
diff --git a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/uncommon_process_query_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/uncommon_process_query_tab_body.tsx
index 1a3e7f31a03fc..d5a5043b9efcf 100644
--- a/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/uncommon_process_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/explore/hosts/pages/navigation/uncommon_process_query_tab_body.tsx
@@ -61,4 +61,4 @@ export const UncommonProcessQueryTabBody = ({
);
};
-UncommonProcessQueryTabBody.dispalyName = 'UncommonProcessQueryTabBody';
+UncommonProcessQueryTabBody.displayName = 'UncommonProcessQueryTabBody';
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.test.tsx
index 2945d3cc5d862..213de1f2d80e2 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.test.tsx
@@ -5,108 +5,29 @@
* 2.0.
*/
-import { useNetworkKpiDns } from '../../../containers/kpi_network/dns';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { NetworkKpiDns } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { dnsStatItems, NetworkKpiDns } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_network/dns');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
describe('DNS KPI', () => {
- const mockUseNetworkKpiDns = useNetworkKpiDns as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseNetworkKpiDns.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseNetworkKpiDns.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseNetworkKpiDns.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
-
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(dnsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.tsx
index 9069c0b44276b..c23f76317217e 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/dns/index.tsx
@@ -5,27 +5,23 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiDnsQueriesLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_dns_queries';
-import { useNetworkKpiDns, ID } from '../../../containers/kpi_network/dns';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import type { NetworkKpiProps } from '../types';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-export const fieldsMapping: Readonly = [
+export const ID = 'networkKpiDnsQuery';
+
+export const dnsStatItems: Readonly = [
{
key: 'dnsQueries',
fields: [
{
key: 'dnsQueries',
- value: null,
lensAttributes: kpiDnsQueriesLensAttributes,
},
],
@@ -33,52 +29,8 @@ export const fieldsMapping: Readonly = [
},
];
-const NetworkKpiDnsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiDns({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const NetworkKpiDnsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const NetworkKpiDns = React.memo(NetworkKpiDnsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/index.tsx
index b6b87e9193dd2..811d11da94bc5 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/index.tsx
@@ -15,73 +15,31 @@ import { NetworkKpiUniqueFlows } from './unique_flows';
import { NetworkKpiUniquePrivateIps } from './unique_private_ips';
import type { NetworkKpiProps } from './types';
-export const NetworkKpiComponent = React.memo(
- ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-);
+export const NetworkKpiComponent = React.memo(({ from, to }) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+));
NetworkKpiComponent.displayName = 'NetworkKpiComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts
deleted file mode 100644
index 4f77abd79dfd4..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/mock.ts
+++ /dev/null
@@ -1,158 +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 { NetworkKpiStrategyResponse } from '../../../../../common/search_strategy';
-import type { StatItems } from '../../../components/stat_items';
-import { kpiUniquePrivateIpsAreaLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area';
-import { kpiUniquePrivateIpsBarLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar';
-import { kpiUniquePrivateIpsDestinationMetricLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric';
-import { kpiUniquePrivateIpsSourceMetricLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric';
-
-export const mockUpdateDateRange = jest.fn();
-
-export const mockData: NetworkKpiStrategyResponse = {
- networkEvents: 16,
- uniqueFlowId: 10277307,
- uniqueSourcePrivateIps: 383,
- uniqueSourcePrivateIpsHistogram: [
- {
- x: new Date('2019-02-09T16:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-09T19:00:00.000Z').valueOf(),
- y: 0,
- },
- ],
- uniqueDestinationPrivateIps: 18,
- uniqueDestinationPrivateIpsHistogram: [
- {
- x: new Date('2019-02-09T16:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-09T19:00:00.000Z').valueOf(),
- y: 0,
- },
- ],
- dnsQueries: 278,
- tlsHandshakes: 10000,
-};
-
-const mockMappingItems: StatItems = {
- key: 'UniqueIps',
- fields: [
- {
- key: 'uniqueSourcePrivateIps',
- value: null,
- name: 'Src.',
- description: 'source',
- color: '#D36086',
- icon: 'visMapCoordinate',
- },
- {
- key: 'uniqueDestinationPrivateIps',
- value: null,
- name: 'Dest.',
- description: 'destination',
- color: '#9170B8',
- icon: 'visMapCoordinate',
- },
- ],
- description: 'Unique private IPs',
- enableAreaChart: true,
- enableBarChart: true,
-};
-
-export const mockNoChartMappings: Readonly = [
- {
- ...mockMappingItems,
- enableAreaChart: false,
- enableBarChart: false,
- },
-];
-
-export const mockEnableChartsData = {
- areaChart: [
- {
- key: 'uniqueSourcePrivateIpsHistogram',
- value: [
- { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 8 },
- {
- x: new Date('2019-02-09T19:00:00.000Z').valueOf(),
- y: 0,
- },
- ],
- name: 'Src.',
- description: 'source',
- color: '#D36086',
- icon: 'visMapCoordinate',
- },
- {
- key: 'uniqueDestinationPrivateIpsHistogram',
- value: [
- { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 8 },
- { x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0 },
- ],
- name: 'Dest.',
- description: 'destination',
- color: '#9170B8',
- icon: 'visMapCoordinate',
- },
- ],
- barChart: [
- {
- key: 'uniqueSourcePrivateIps',
- color: '#D36086',
- value: [
- {
- x: 'Src.',
- y: 383,
- g: 'uniqueSourcePrivateIps',
- y0: 0,
- },
- ],
- },
- {
- key: 'uniqueDestinationPrivateIps',
- color: '#9170B8',
- value: [{ x: 'Dest.', y: 18, g: 'uniqueDestinationPrivateIps', y0: 0 }],
- },
- ],
- description: 'Unique private IPs',
- enableAreaChart: true,
- enableBarChart: true,
- fields: [
- {
- key: 'uniqueSourcePrivateIps',
- value: 383,
- name: 'Src.',
- description: 'source',
- color: '#D36086',
- icon: 'visMapCoordinate',
- lensAttributes: kpiUniquePrivateIpsSourceMetricLensAttributes,
- },
- {
- key: 'uniqueDestinationPrivateIps',
- value: 18,
- name: 'Dest.',
- description: 'destination',
- color: '#9170B8',
- icon: 'visMapCoordinate',
- lensAttributes: kpiUniquePrivateIpsDestinationMetricLensAttributes,
- },
- ],
- from: '2019-06-15T06:00:00.000Z',
- id: 'statItem',
- loading: false,
- statKey: 'uniqueIps',
- setQuerySkip: jest.fn(),
- to: '2019-06-18T06:00:00.000Z',
- updateDateRange: mockUpdateDateRange,
- areaChartLensAttributes: kpiUniquePrivateIpsAreaLensAttributes,
- barChartLensAttributes: kpiUniquePrivateIpsBarLensAttributes,
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.test.tsx
index bd1bc90855894..f0757a7c07cc4 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useNetworkKpiNetworkEvents } from '../../../containers/kpi_network/network_events';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { NetworkKpiNetworkEvents } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { networkEventsStatsItems, NetworkKpiNetworkEvents } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_network/network_events');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Network events KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('Network Events KPI', () => {
- const mockUseNetworkKpiNetworkEvents = useNetworkKpiNetworkEvents as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseNetworkKpiNetworkEvents.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseNetworkKpiNetworkEvents.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseNetworkKpiNetworkEvents.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(networkEventsStatsItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.tsx
index 08173486c5d10..31b3396a567d3 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/network_events/index.tsx
@@ -5,30 +5,26 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import { euiPaletteColorBlind } from '@elastic/eui';
import type { StatItems } from '../../../../components/stat_items';
-import { ID, useNetworkKpiNetworkEvents } from '../../../containers/kpi_network/network_events';
import type { NetworkKpiProps } from '../types';
import * as i18n from './translations';
import { kpiNetworkEventsLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_network_events';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
+import { KpiBaseComponent } from '../../../../components/kpi';
+
+export const ID = 'networkKpiNetworkEventsQuery';
const euiVisColorPalette = euiPaletteColorBlind();
const euiColorVis1 = euiVisColorPalette[1];
-export const fieldsMapping: Readonly = [
+export const networkEventsStatsItems: Readonly = [
{
key: 'networkEvents',
fields: [
{
key: 'networkEvents',
- value: null,
color: euiColorVis1,
lensAttributes: kpiNetworkEventsLensAttributes,
},
@@ -37,52 +33,8 @@ export const fieldsMapping: Readonly = [
},
];
-const NetworkKpiNetworkEventsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiNetworkEvents({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const NetworkKpiNetworkEventsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const NetworkKpiNetworkEvents = React.memo(NetworkKpiNetworkEventsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.test.tsx
index 965be78e4675f..37fc612b08cc5 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useNetworkKpiTlsHandshakes } from '../../../containers/kpi_network/tls_handshakes';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { NetworkKpiTlsHandshakes } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { tlsStatItems, NetworkKpiTlsHandshakes } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_network/tls_handshakes');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('TLS handshakes KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('TLS Handshakes KPI', () => {
- const mockUseNetworkKpiTlsHandshakes = useNetworkKpiTlsHandshakes as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseNetworkKpiTlsHandshakes.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseNetworkKpiTlsHandshakes.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseNetworkKpiTlsHandshakes.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].searchSessionId).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(tlsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.tsx
index 08d3d401307a6..a3d99613861f2 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/tls_handshakes/index.tsx
@@ -5,26 +5,22 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiTlsHandshakesLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_tls_handshakes';
-import { useNetworkKpiTlsHandshakes, ID } from '../../../containers/kpi_network/tls_handshakes';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import type { NetworkKpiProps } from '../types';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-export const fieldsMapping: Readonly = [
+export const ID = 'networkKpiTlsHandshakesQuery';
+
+export const tlsStatItems: Readonly = [
{
key: 'tlsHandshakes',
fields: [
{
key: 'tlsHandshakes',
- value: null,
lensAttributes: kpiTlsHandshakesLensAttributes,
},
],
@@ -32,52 +28,8 @@ export const fieldsMapping: Readonly = [
},
];
-const NetworkKpiTlsHandshakesComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiTlsHandshakes({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const NetworkKpiTlsHandshakesComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const NetworkKpiTlsHandshakes = React.memo(NetworkKpiTlsHandshakesComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/types.ts b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/types.ts
index 84f28481a8c56..3a67e587c1ca8 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/types.ts
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/types.ts
@@ -5,15 +5,7 @@
* 2.0.
*/
-import type { UpdateDateRange } from '../../../../common/components/charts/common';
-import type { GlobalTimeArgs } from '../../../../common/containers/use_global_time';
-
export interface NetworkKpiProps {
- filterQuery?: string;
from: string;
- indexNames: string[];
to: string;
- updateDateRange: UpdateDateRange;
- setQuery: GlobalTimeArgs['setQuery'];
- skip: boolean;
}
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.test.tsx
index 494c7e43abd5d..755af7b7425d7 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useNetworkKpiUniqueFlows } from '../../../containers/kpi_network/unique_flows';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { NetworkKpiUniqueFlows } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { uniqueFlowsStatItems, NetworkKpiUniqueFlows } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_network/unique_flows');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Network unique flow KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('Unique Flows KPI', () => {
- const mockUseNetworkKpiUniqueFlows = useNetworkKpiUniqueFlows as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseNetworkKpiUniqueFlows.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseNetworkKpiUniqueFlows.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseNetworkKpiUniqueFlows.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].searchSessionId).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(uniqueFlowsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.tsx
index b7b145e9681ba..89ad0d18100f8 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_flows/index.tsx
@@ -5,26 +5,22 @@
* 2.0.
*/
-import React, { useState, useEffect } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiUniqueFlowIdsLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_flow_ids';
-import { useNetworkKpiUniqueFlows, ID } from '../../../containers/kpi_network/unique_flows';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import type { NetworkKpiProps } from '../types';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-export const fieldsMapping: Readonly = [
+export const ID = 'networkKpiUniqueFlowsQuery';
+
+export const uniqueFlowsStatItems: Readonly = [
{
key: 'uniqueFlowId',
fields: [
{
key: 'uniqueFlowId',
- value: null,
lensAttributes: kpiUniqueFlowIdsLensAttributes,
},
],
@@ -32,52 +28,8 @@ export const fieldsMapping: Readonly = [
},
];
-const NetworkKpiUniqueFlowsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiUniqueFlows({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const NetworkKpiUniqueFlowsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const NetworkKpiUniqueFlows = React.memo(NetworkKpiUniqueFlowsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.test.tsx
index 7e7309c85b48c..d19ae024fdaa4 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.test.tsx
@@ -5,107 +5,29 @@
* 2.0.
*/
-import { useNetworkKpiUniquePrivateIps } from '../../../containers/kpi_network/unique_private_ips';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { NetworkKpiUniquePrivateIps } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { uniquePrivateIpsStatItems, NetworkKpiUniquePrivateIps } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/kpi_network/unique_private_ips');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Network unique private ips KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('Unique Private IPs KPI', () => {
- const mockUseNetworkKpiUniquePrivateIps = useNetworkKpiUniquePrivateIps as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseNetworkKpiUniquePrivateIps.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseNetworkKpiUniquePrivateIps.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseNetworkKpiUniquePrivateIps.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].searchSessionId).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(uniquePrivateIpsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx
index e4e9b7fc7a0b8..2c84c27899084 100644
--- a/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/components/kpi_network/unique_private_ips/index.tsx
@@ -5,37 +5,29 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import { euiPaletteColorBlind } from '@elastic/eui';
import type { StatItems } from '../../../../components/stat_items';
-import {
- useNetworkKpiUniquePrivateIps,
- ID,
-} from '../../../containers/kpi_network/unique_private_ips';
import type { NetworkKpiProps } from '../types';
import * as i18n from './translations';
import { kpiUniquePrivateIpsSourceMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_source_metric';
import { kpiUniquePrivateIpsDestinationMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_destination_metric';
import { kpiUniquePrivateIpsAreaLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_area';
import { kpiUniquePrivateIpsBarLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/network/kpi_unique_private_ips_bar';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
+import { KpiBaseComponent } from '../../../../components/kpi';
const euiVisColorPalette = euiPaletteColorBlind();
const euiColorVis2 = euiVisColorPalette[2];
const euiColorVis3 = euiVisColorPalette[3];
+export const ID = 'networkKpiUniquePrivateIpsQuery';
-export const fieldsMapping: Readonly = [
+export const uniquePrivateIpsStatItems: Readonly = [
{
key: 'uniqueIps',
fields: [
{
key: 'uniqueSourcePrivateIps',
- value: null,
name: i18n.SOURCE_CHART_LABEL,
description: i18n.SOURCE_UNIT_LABEL,
color: euiColorVis2,
@@ -44,7 +36,6 @@ export const fieldsMapping: Readonly = [
},
{
key: 'uniqueDestinationPrivateIps',
- value: null,
name: i18n.DESTINATION_CHART_LABEL,
description: i18n.DESTINATION_UNIT_LABEL,
color: euiColorVis3,
@@ -60,52 +51,8 @@ export const fieldsMapping: Readonly = [
},
];
-const NetworkKpiUniquePrivateIpsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useNetworkKpiUniquePrivateIps({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const NetworkKpiUniquePrivateIpsComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const NetworkKpiUniquePrivateIps = React.memo(NetworkKpiUniquePrivateIpsComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.test.tsx
deleted file mode 100644
index 869baa2203f82..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useNetworkKpiDns } from '.';
-
-describe('kpi network - dns', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useNetworkKpiDns(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx
deleted file mode 100644
index 417c3678fb06a..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/index.tsx
+++ /dev/null
@@ -1,154 +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 deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { NetworkKpiDnsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { NetworkKpiDnsStrategyResponse } from '../../../../../../common/search_strategy';
-import { NetworkKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'networkKpiDnsQuery';
-
-export interface NetworkKpiDnsArgs {
- dnsQueries: number;
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseNetworkKpiDns {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useNetworkKpiDns = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseNetworkKpiDns): [boolean, NetworkKpiDnsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [networkKpiDnsRequest, setNetworkKpiDnsRequest] =
- useState(null);
-
- const [networkKpiDnsResponse, setNetworkKpiDnsResponse] = useState({
- dnsQueries: 0,
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError } = useAppToasts();
-
- const networkKpiDnsSearch = useCallback(
- (request: NetworkKpiDnsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- setLoading(false);
- setNetworkKpiDnsResponse((prevResponse) => ({
- ...prevResponse,
- dnsQueries: response.dnsQueries,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_NETWORK_KPI_DNS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, skip]
- );
-
- useEffect(() => {
- setNetworkKpiDnsRequest((prevRequest) => {
- const myRequest: NetworkKpiDnsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: NetworkKpiQueries.dns,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- networkKpiDnsSearch(networkKpiDnsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [networkKpiDnsRequest, networkKpiDnsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, networkKpiDnsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/translations.ts b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/translations.ts
deleted file mode 100644
index 2ba68b0939cb7..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/dns/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_NETWORK_KPI_DNS = i18n.translate(
- 'xpack.securitySolution.networkKpiDns.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on network kpi dns search`,
- }
-);
-
-export const FAIL_NETWORK_KPI_DNS = i18n.translate(
- 'xpack.securitySolution.networkKpiDns.failSearchDescription',
- {
- defaultMessage: `Failed to run search on network kpi dns`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.test.tsx
deleted file mode 100644
index 05f193dd5f8ac..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useNetworkKpiNetworkEvents } from '.';
-
-describe('kpi network - network events', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useNetworkKpiNetworkEvents(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx
deleted file mode 100644
index e675196aa0a80..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/index.tsx
+++ /dev/null
@@ -1,158 +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 deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { NetworkKpiEventsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { NetworkKpiNetworkEventsStrategyResponse } from '../../../../../../common/search_strategy';
-import { NetworkKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'networkKpiNetworkEventsQuery';
-
-export interface NetworkKpiNetworkEventsArgs {
- networkEvents: number;
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseNetworkKpiNetworkEvents {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useNetworkKpiNetworkEvents = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseNetworkKpiNetworkEvents): [boolean, NetworkKpiNetworkEventsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [networkKpiNetworkEventsRequest, setNetworkKpiNetworkEventsRequest] =
- useState(null);
-
- const [networkKpiNetworkEventsResponse, setNetworkKpiNetworkEventsResponse] =
- useState({
- networkEvents: 0,
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError } = useAppToasts();
-
- const networkKpiNetworkEventsSearch = useCallback(
- (request: NetworkKpiEventsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search(
- request,
- {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- }
- )
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- setLoading(false);
- setNetworkKpiNetworkEventsResponse((prevResponse) => ({
- ...prevResponse,
- networkEvents: response.networkEvents,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_NETWORK_KPI_NETWORK_EVENTS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, skip]
- );
-
- useEffect(() => {
- setNetworkKpiNetworkEventsRequest((prevRequest) => {
- const myRequest: NetworkKpiEventsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: NetworkKpiQueries.networkEvents,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- networkKpiNetworkEventsSearch(networkKpiNetworkEventsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [networkKpiNetworkEventsRequest, networkKpiNetworkEventsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, networkKpiNetworkEventsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/translations.ts b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/translations.ts
deleted file mode 100644
index 5067adf3768c7..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/network_events/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_NETWORK_KPI_NETWORK_EVENTS = i18n.translate(
- 'xpack.securitySolution.networkKpiNetworkEvents.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on network kpi network events search`,
- }
-);
-
-export const FAIL_NETWORK_KPI_NETWORK_EVENTS = i18n.translate(
- 'xpack.securitySolution.networkKpiNetworkEvents.failSearchDescription',
- {
- defaultMessage: `Failed to run search on network kpi network events`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.test.tsx
deleted file mode 100644
index c7e3b8c2f3aa5..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useNetworkKpiTlsHandshakes } from '.';
-
-describe('kpi network - tls handshakes', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useNetworkKpiTlsHandshakes(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx
deleted file mode 100644
index a94041f640d93..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/index.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { NetworkKpiTlsHandshakesRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { NetworkKpiTlsHandshakesStrategyResponse } from '../../../../../../common/search_strategy';
-import { NetworkKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'networkKpiTlsHandshakesQuery';
-
-export interface NetworkKpiTlsHandshakesArgs {
- tlsHandshakes: number;
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseNetworkKpiTlsHandshakes {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useNetworkKpiTlsHandshakes = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseNetworkKpiTlsHandshakes): [boolean, NetworkKpiTlsHandshakesArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [networkKpiTlsHandshakesRequest, setNetworkKpiTlsHandshakesRequest] =
- useState(null);
-
- const [networkKpiTlsHandshakesResponse, setNetworkKpiTlsHandshakesResponse] =
- useState({
- tlsHandshakes: 0,
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError } = useAppToasts();
-
- const networkKpiTlsHandshakesSearch = useCallback(
- (request: NetworkKpiTlsHandshakesRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search<
- NetworkKpiTlsHandshakesRequestOptionsInput,
- NetworkKpiTlsHandshakesStrategyResponse
- >(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- setLoading(false);
- setNetworkKpiTlsHandshakesResponse((prevResponse) => ({
- ...prevResponse,
- tlsHandshakes: response.tlsHandshakes,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_NETWORK_KPI_TLS_HANDSHAKES,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, skip]
- );
-
- useEffect(() => {
- setNetworkKpiTlsHandshakesRequest((prevRequest) => {
- const myRequest: NetworkKpiTlsHandshakesRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: NetworkKpiQueries.tlsHandshakes,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- networkKpiTlsHandshakesSearch(networkKpiTlsHandshakesRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [networkKpiTlsHandshakesRequest, networkKpiTlsHandshakesSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, networkKpiTlsHandshakesResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/translations.ts b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/translations.ts
deleted file mode 100644
index eb9bd43b601c4..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/tls_handshakes/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_NETWORK_KPI_TLS_HANDSHAKES = i18n.translate(
- 'xpack.securitySolution.networkKpiTlsHandshakes.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on network kpi tls handshakes search`,
- }
-);
-
-export const FAIL_NETWORK_KPI_TLS_HANDSHAKES = i18n.translate(
- 'xpack.securitySolution.networkKpiTlsHandshakes.failSearchDescription',
- {
- defaultMessage: `Failed to run search on network kpi tls handshakes`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.test.tsx
deleted file mode 100644
index 75004d1b92ef2..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useNetworkKpiUniqueFlows } from '.';
-
-describe('kpi network - unique flows', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useNetworkKpiUniqueFlows(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx
deleted file mode 100644
index e997a9eb94e1d..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/index.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { NetworkKpiUniqueFlowsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { NetworkKpiUniqueFlowsStrategyResponse } from '../../../../../../common/search_strategy';
-import { NetworkKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'networkKpiUniqueFlowsQuery';
-
-export interface NetworkKpiUniqueFlowsArgs {
- uniqueFlowId: number;
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseNetworkKpiUniqueFlows {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useNetworkKpiUniqueFlows = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseNetworkKpiUniqueFlows): [boolean, NetworkKpiUniqueFlowsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [networkKpiUniqueFlowsRequest, setNetworkKpiUniqueFlowsRequest] =
- useState(null);
-
- const [networkKpiUniqueFlowsResponse, setNetworkKpiUniqueFlowsResponse] =
- useState({
- uniqueFlowId: 0,
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError } = useAppToasts();
-
- const networkKpiUniqueFlowsSearch = useCallback(
- (request: NetworkKpiUniqueFlowsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
- searchSubscription$.current = data.search
- .search(
- request,
- {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- }
- )
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- setLoading(false);
- setNetworkKpiUniqueFlowsResponse((prevResponse) => ({
- ...prevResponse,
- uniqueFlowId: response.uniqueFlowId,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_NETWORK_KPI_UNIQUE_FLOWS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, skip]
- );
-
- useEffect(() => {
- setNetworkKpiUniqueFlowsRequest((prevRequest) => {
- const myRequest: NetworkKpiUniqueFlowsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: NetworkKpiQueries.uniqueFlows,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- networkKpiUniqueFlowsSearch(networkKpiUniqueFlowsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [networkKpiUniqueFlowsRequest, networkKpiUniqueFlowsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, networkKpiUniqueFlowsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/translations.ts b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/translations.ts
deleted file mode 100644
index 5c882a00a03e9..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_flows/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_NETWORK_KPI_UNIQUE_FLOWS = i18n.translate(
- 'xpack.securitySolution.networkKpiUniqueFlows.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on network kpi unique flows search`,
- }
-);
-
-export const FAIL_NETWORK_KPI_UNIQUE_FLOWS = i18n.translate(
- 'xpack.securitySolution.networkKpiUniqueFlows.failSearchDescription',
- {
- defaultMessage: `Failed to run search on network kpi unique flows`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.test.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.test.tsx
deleted file mode 100644
index 0d5ca709b31ee..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useNetworkKpiUniquePrivateIps } from '.';
-
-describe('kpi network - unique private ips', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useNetworkKpiUniquePrivateIps(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx
deleted file mode 100644
index a61d4a859082e..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/index.tsx
+++ /dev/null
@@ -1,171 +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 deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import { isRunningResponse } from '@kbn/data-plugin/common';
-import type { NetworkKpiUniquePrivateIpsRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type {
- NetworkKpiHistogramData,
- NetworkKpiUniquePrivateIpsStrategyResponse,
-} from '../../../../../../common/search_strategy';
-import { NetworkKpiQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'networkKpiUniquePrivateIpsQuery';
-
-export interface NetworkKpiUniquePrivateIpsArgs {
- uniqueDestinationPrivateIps: number;
- uniqueDestinationPrivateIpsHistogram: NetworkKpiHistogramData[] | null;
- uniqueSourcePrivateIps: number;
- uniqueSourcePrivateIpsHistogram: NetworkKpiHistogramData[] | null;
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseNetworkKpiUniquePrivateIps {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useNetworkKpiUniquePrivateIps = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseNetworkKpiUniquePrivateIps): [boolean, NetworkKpiUniquePrivateIpsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [networkKpiUniquePrivateIpsRequest, setNetworkKpiUniquePrivateIpsRequest] =
- useState(null);
-
- const [networkKpiUniquePrivateIpsResponse, setNetworkKpiUniquePrivateIpsResponse] =
- useState({
- uniqueDestinationPrivateIps: 0,
- uniqueDestinationPrivateIpsHistogram: null,
- uniqueSourcePrivateIps: 0,
- uniqueSourcePrivateIpsHistogram: null,
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError } = useAppToasts();
-
- const networkKpiUniquePrivateIpsSearch = useCallback(
- (request: NetworkKpiUniquePrivateIpsRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search<
- NetworkKpiUniquePrivateIpsRequestOptionsInput,
- NetworkKpiUniquePrivateIpsStrategyResponse
- >(request, {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- })
- .subscribe({
- next: (response) => {
- if (!isRunningResponse(response)) {
- setLoading(false);
- setNetworkKpiUniquePrivateIpsResponse((prevResponse) => ({
- ...prevResponse,
- uniqueDestinationPrivateIps: response.uniqueDestinationPrivateIps,
- uniqueDestinationPrivateIpsHistogram:
- response.uniqueDestinationPrivateIpsHistogram,
- uniqueSourcePrivateIps: response.uniqueSourcePrivateIps,
- uniqueSourcePrivateIpsHistogram: response.uniqueSourcePrivateIpsHistogram,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_NETWORK_KPI_UNIQUE_PRIVATE_IPS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, skip]
- );
-
- useEffect(() => {
- setNetworkKpiUniquePrivateIpsRequest((prevRequest) => {
- const myRequest: NetworkKpiUniquePrivateIpsRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: NetworkKpiQueries.uniquePrivateIps,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- networkKpiUniquePrivateIpsSearch(networkKpiUniquePrivateIpsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [networkKpiUniquePrivateIpsRequest, networkKpiUniquePrivateIpsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, networkKpiUniquePrivateIpsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/translations.ts b/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/translations.ts
deleted file mode 100644
index f8d46c62d948c..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/network/containers/kpi_network/unique_private_ips/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_NETWORK_KPI_UNIQUE_PRIVATE_IPS = i18n.translate(
- 'xpack.securitySolution.networkKpiUniquePrivateIps.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on network kpi unique private ips search`,
- }
-);
-
-export const FAIL_NETWORK_KPI_UNIQUE_PRIVATE_IPS = i18n.translate(
- 'xpack.securitySolution.networkKpiUniquePrivateIps.failSearchDescription',
- {
- defaultMessage: `Failed to run search on network kpi unique private ips`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/network/pages/navigation/dns_query_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/network/pages/navigation/dns_query_tab_body.tsx
index c2db5e1794ff1..1e1870ee17594 100644
--- a/x-pack/plugins/security_solution/public/explore/network/pages/navigation/dns_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/pages/navigation/dns_query_tab_body.tsx
@@ -20,7 +20,6 @@ import type {
} from '../../../../common/components/matrix_histogram/types';
import * as i18n from './translations';
import { MatrixHistogram } from '../../../../common/components/matrix_histogram';
-import { MatrixHistogramType } from '../../../../../common/search_strategy/security_solution';
import { networkSelectors } from '../../store';
import { useShallowEqualSelector } from '../../../../common/hooks/use_selector';
import { getDnsTopDomainsLensAttributes } from '../../../../common/components/visualization_actions/lens_attributes/network/dns_top_domains';
@@ -42,8 +41,6 @@ const DEFAULT_STACK_BY = 'dns.question.registered_domain';
export const histogramConfigs: Omit = {
defaultStackByOption:
dnsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? dnsStackByOptions[0],
- errorMessage: i18n.ERROR_FETCHING_DNS_DATA,
- histogramType: MatrixHistogramType.dns,
stackByOptions: dnsStackByOptions,
subtitle: undefined,
getLensAttributes: getDnsTopDomainsLensAttributes,
@@ -109,9 +106,7 @@ const DnsQueryTabBodyComponent: React.FC = ({
isPtrIncluded={isPtrIncluded}
endDate={endDate}
filterQuery={filterQuery}
- indexNames={indexNames}
setQuery={setQuery}
- showLegend={true}
startDate={startDate}
{...dnsHistogramConfigs}
/>
diff --git a/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx b/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx
index 3ca4d2c7a4d2b..5cb2d55f4d796 100644
--- a/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx
+++ b/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx
@@ -8,7 +8,6 @@
import { EuiPanel, EuiSpacer, EuiWindowEvent } from '@elastic/eui';
import { noop } from 'lodash/fp';
import React, { useCallback, useMemo, useRef } from 'react';
-import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
import styled from 'styled-components';
@@ -17,7 +16,6 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common';
import { dataTableSelectors, tableDefaults, TableId } from '@kbn/securitysolution-data-table';
import { InputsModelId } from '../../../common/store/inputs/constants';
import { SecurityPageName } from '../../../app/types';
-import type { UpdateDateRange } from '../../../common/components/charts/common';
import { EmbeddedMap } from '../components/embeddables/embedded_map';
import { FiltersGlobal } from '../../../common/components/filters_global';
import { HeaderPage } from '../../../common/components/header_page';
@@ -33,7 +31,6 @@ import { LastEventIndexKey } from '../../../../common/search_strategy';
import { useKibana } from '../../../common/lib/kibana';
import { convertToBuildEsQuery } from '../../../common/lib/kuery';
import { inputsSelectors } from '../../../common/store';
-import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions';
import { SpyRoute } from '../../../common/utils/route/spy_routes';
import { Display } from '../../hosts/pages/display';
import { networkModel } from '../store';
@@ -64,7 +61,6 @@ const ID = 'NetworkQueryId';
const NetworkComponent = React.memo(
({ hasMlUserPermissions, capabilitiesFetched }) => {
- const dispatch = useDispatch();
const containerElement = useRef(null);
const getTable = useMemo(() => dataTableSelectors.getTableByIdSelector(), []);
const graphEventId = useShallowEqualSelector(
@@ -92,23 +88,6 @@ const NetworkComponent = React.memo(
return globalFilters;
}, [tabName, globalFilters]);
- const updateDateRange = useCallback(
- ({ x }) => {
- if (!x) {
- return;
- }
- const [min, max] = x;
- dispatch(
- setAbsoluteRangeDatePicker({
- id: InputsModelId.global,
- from: new Date(min).toISOString(),
- to: new Date(max).toISOString(),
- })
- );
- },
- [dispatch]
- );
-
const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } =
useSourcererDataView();
@@ -193,15 +172,7 @@ const NetworkComponent = React.memo(
>
)}
-
+
{capabilitiesFetched && !isInitializing ? (
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.test.tsx b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.test.tsx
index 68d03def9fd8d..2697e4675e4c3 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.test.tsx
@@ -5,106 +5,29 @@
* 2.0.
*/
-import { useUsersKpiAuthentications } from '../../../containers/users/authentications';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { UsersKpiAuthentications } from '.';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { authenticationsStatItems, UsersKpiAuthentications } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
+
+jest.mock('../../../../components/kpi');
+
+describe('User authentications KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../containers/users/authentications');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
-describe('Authentications KPI', () => {
- const mockUseHostsKpiAuthentications = useUsersKpiAuthentications as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseHostsKpiAuthentications.mockReturnValue([
- false,
- {
- id: '123',
- inspect: {
- dsl: [],
- response: [],
- },
- refetch: mockRefetch,
- },
- ]);
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseHostsKpiAuthentications.mock.calls[0][0].skip).toEqual(false);
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseHostsKpiAuthentications.mock.calls[0][0].skip).toEqual(true);
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(authenticationsStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.tsx b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.tsx
index 4cd4e4fbfd07f..a1b9066a721ac 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/authentications/index.tsx
@@ -5,28 +5,25 @@
* 2.0.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
import { kpiUserAuthenticationsAreaLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_area';
import { kpiUserAuthenticationsBarLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_bar';
import { kpiUserAuthenticationsMetricSuccessLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_user_authentications_metric_success';
import { kpiUserAuthenticationsMetricFailureLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_user_authentication_metric_failure';
-import { useUsersKpiAuthentications, ID } from '../../../containers/users/authentications';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import type { UsersKpiProps } from '../types';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
+
+const ID = 'usersKpiAuthentications';
enum ChartColors {
authenticationsSuccess = '#54B399',
authenticationsFailure = '#E7664C',
}
-export const fieldsMapping: Readonly = [
+export const authenticationsStatItems: Readonly = [
{
key: 'authentication',
fields: [
@@ -34,7 +31,6 @@ export const fieldsMapping: Readonly = [
key: 'authenticationsSuccess',
name: i18n.SUCCESS_CHART_LABEL,
description: i18n.SUCCESS_UNIT_LABEL,
- value: null,
color: ChartColors.authenticationsSuccess,
icon: 'check',
lensAttributes: kpiUserAuthenticationsMetricSuccessLensAttributes,
@@ -43,7 +39,6 @@ export const fieldsMapping: Readonly = [
key: 'authenticationsFailure',
name: i18n.FAIL_CHART_LABEL,
description: i18n.FAIL_UNIT_LABEL,
- value: null,
color: ChartColors.authenticationsFailure,
icon: 'cross',
lensAttributes: kpiUserAuthenticationsMetricFailureLensAttributes,
@@ -57,52 +52,8 @@ export const fieldsMapping: Readonly = [
},
];
-const UsersKpiAuthenticationsComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const [loading, { refetch, id, inspect, ...data }] = useUsersKpiAuthentications({
- filterQuery,
- endDate: to,
- indexNames,
- startDate: from,
- skip: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: id,
- });
-
- return (
-
- );
+const UsersKpiAuthenticationsComponent: React.FC = ({ from, to }) => {
+ return ;
};
UsersKpiAuthenticationsComponent.displayName = 'UsersKpiAuthenticationsComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/index.tsx b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/index.tsx
index b7052bef14ba2..3870e56fc9e60 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/index.tsx
@@ -11,34 +11,16 @@ import type { UsersKpiProps } from './types';
import { UsersKpiAuthentications } from './authentications';
import { TotalUsersKpi } from './total_users';
-export const UsersKpiComponent = React.memo(
- ({ filterQuery, from, indexNames, to, setQuery, skip, updateDateRange }) => (
-
-
-
-
+export const UsersKpiComponent = React.memo(({ from, to }) => (
+
+
+
+
-
-
-
-
- )
-);
+
+
+
+
+));
UsersKpiComponent.displayName = 'UsersKpiComponent';
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.test.tsx b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.test.tsx
index a02b27d06ac99..30bb37782889f 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.test.tsx
@@ -5,109 +5,29 @@
* 2.0.
*/
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import { render } from '@testing-library/react';
import { TestProviders } from '../../../../../common/mock';
import React from 'react';
-import { TotalUsersKpi } from '.';
-import { useSearchStrategy } from '../../../../../common/containers/use_search_strategy';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { usersStatItems, TotalUsersKpi } from '.';
+import { KpiBaseComponent } from '../../../../components/kpi';
-jest.mock('../../../../../common/containers/query_toggle');
-jest.mock('../../../../../common/containers/use_search_strategy');
-jest.mock('../../../../hosts/components/kpi_hosts/common', () => ({
- KpiBaseComponentManage: jest
- .fn()
- .mockReturnValue( ),
-}));
-jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
- useIsExperimentalFeatureEnabled: jest.fn(),
-}));
-jest.mock('../../../../../common/components/page/use_refetch_by_session', () => ({
- useRefetchByRestartingSession: jest.fn(),
-}));
+jest.mock('../../../../components/kpi');
+
+describe('Users KPI', () => {
+ const from = new Date('2023-12-30').toISOString();
+ const to = new Date('2023-12-31').toISOString();
+ const MockKpiBaseComponent = KpiBaseComponent as unknown as jest.Mock;
-describe('Total Users KPI', () => {
- const mockUseSearchStrategy = useSearchStrategy as jest.Mock;
- const mockUseQueryToggle = useQueryToggle as jest.Mock;
- const MockKpiBaseComponentManage = KpiBaseComponentManage as jest.Mock;
- const mockRefetchByRestartingSession = jest.fn();
- const mockSession = { current: { start: jest.fn(() => 'mockNewSearchSessionId') } };
- const mockRefetch = jest.fn();
- const defaultProps = {
- from: '2019-06-25T04:31:59.345Z',
- to: '2019-06-25T06:31:59.345Z',
- indexNames: [],
- updateDateRange: jest.fn(),
- setQuery: jest.fn(),
- skip: false,
- };
- const mockSearch = jest.fn();
- beforeEach(() => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: jest.fn() });
- mockUseSearchStrategy.mockReturnValue({
- result: [],
- loading: false,
- inspect: {
- dsl: [],
- response: [],
- },
- search: mockSearch,
- refetch: mockRefetch,
- });
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(false);
- (useRefetchByRestartingSession as jest.Mock).mockReturnValue({
- session: mockSession,
- searchSessionId: 'mockSearchSessionId',
- refetchByRestartingSession: mockRefetchByRestartingSession,
- });
- });
afterEach(() => {
jest.clearAllMocks();
});
- it('toggleStatus=true, do not skip', () => {
- render(
-
-
-
- );
- expect(mockUseSearchStrategy.mock.calls[0][0].abort).toEqual(false);
- expect(mockSearch).toHaveBeenCalled();
- });
- it('toggleStatus=false, skip', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(mockUseSearchStrategy.mock.calls[0][0].abort).toEqual(true);
- expect(mockSearch).not.toHaveBeenCalled();
- });
- it('Refetches data', () => {
- mockUseQueryToggle.mockReturnValue({ toggleStatus: false, setToggleStatus: jest.fn() });
- render(
-
-
-
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(mockRefetch);
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toBeUndefined();
- });
- it('Refetch by restarting search session ID if isChartEmbeddablesEnabled = true', () => {
- (useIsExperimentalFeatureEnabled as jest.Mock).mockReturnValue(true);
- render(
-
-
-
- );
-
- expect(MockKpiBaseComponentManage.mock.calls[0][0].refetch).toEqual(
- mockRefetchByRestartingSession
- );
- expect(MockKpiBaseComponentManage.mock.calls[0][0].session).toEqual(mockSession);
+ it('renders correctly', () => {
+ render( , {
+ wrapper: TestProviders,
+ });
+ expect(MockKpiBaseComponent.mock.calls[0][0].statItems).toEqual(usersStatItems);
+ expect(MockKpiBaseComponent.mock.calls[0][0].from).toEqual(from);
+ expect(MockKpiBaseComponent.mock.calls[0][0].to).toEqual(to);
});
});
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.tsx b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.tsx
index 25326c38b1346..195bc43337f9d 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/total_users/index.tsx
@@ -6,30 +6,23 @@
*/
import { euiPaletteColorBlind } from '@elastic/eui';
-import React, { useEffect, useState } from 'react';
-import { UsersQueries } from '../../../../../../common/search_strategy/security_solution/users';
+import React from 'react';
import type { StatItems } from '../../../../components/stat_items';
-import { useSearchStrategy } from '../../../../../common/containers/use_search_strategy';
-import { KpiBaseComponentManage } from '../../../../hosts/components/kpi_hosts/common';
+import { KpiBaseComponent } from '../../../../components/kpi';
import { kpiTotalUsersMetricLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_total_users_metric';
import { kpiTotalUsersAreaLensAttributes } from '../../../../../common/components/visualization_actions/lens_attributes/users/kpi_total_users_area';
import * as i18n from './translations';
-import { useQueryToggle } from '../../../../../common/containers/query_toggle';
import type { UsersKpiProps } from '../types';
-import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
-import { useRefetchByRestartingSession } from '../../../../../common/components/page/use_refetch_by_session';
-import { InputsModelId } from '../../../../../common/store/inputs/constants';
const euiVisColorPalette = euiPaletteColorBlind();
const euiColorVis1 = euiVisColorPalette[1];
-export const fieldsMapping: Readonly = [
+export const usersStatItems: Readonly = [
{
key: 'users',
fields: [
{
key: 'users',
- value: null,
color: euiColorVis1,
icon: 'storage',
lensAttributes: kpiTotalUsersMetricLensAttributes,
@@ -41,68 +34,10 @@ export const fieldsMapping: Readonly = [
},
];
-const QUERY_ID = 'TotalUsersKpiQuery';
+const ID = 'TotalUsersKpiQuery';
-const TotalUsersKpiComponent: React.FC = ({
- filterQuery,
- from,
- indexNames,
- to,
- updateDateRange,
- setQuery,
- skip,
-}) => {
- const { toggleStatus } = useQueryToggle(QUERY_ID);
- const [querySkip, setQuerySkip] = useState(skip || !toggleStatus);
- const isChartEmbeddablesEnabled = useIsExperimentalFeatureEnabled('chartEmbeddablesEnabled');
-
- useEffect(() => {
- setQuerySkip(skip || !toggleStatus);
- }, [skip, toggleStatus]);
-
- const { loading, result, search, refetch, inspect } =
- useSearchStrategy({
- factoryQueryType: UsersQueries.kpiTotalUsers,
- initialResult: { users: 0, usersHistogram: [] },
- errorMessage: i18n.ERROR_USERS_KPI,
- abort: querySkip || isChartEmbeddablesEnabled,
- });
-
- const { session, refetchByRestartingSession } = useRefetchByRestartingSession({
- inputId: InputsModelId.global,
- queryId: QUERY_ID,
- });
-
- useEffect(() => {
- if (!querySkip) {
- search({
- filterQuery,
- defaultIndex: indexNames,
- timerange: {
- interval: '12h',
- from,
- to,
- },
- });
- }
- }, [search, from, to, filterQuery, indexNames, querySkip]);
-
- return (
-
- );
+const TotalUsersKpiComponent: React.FC = ({ from, to }) => {
+ return ;
};
export const TotalUsersKpi = React.memo(TotalUsersKpiComponent);
diff --git a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/types.ts b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/types.ts
index d9bf1487877df..764cf05521357 100644
--- a/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/types.ts
+++ b/x-pack/plugins/security_solution/public/explore/users/components/kpi_users/types.ts
@@ -5,15 +5,7 @@
* 2.0.
*/
-import type { UpdateDateRange } from '../../../../common/components/charts/common';
-import type { GlobalTimeArgs } from '../../../../common/containers/use_global_time';
-
export interface UsersKpiProps {
- filterQuery?: string;
from: string;
to: string;
- indexNames: string[];
- updateDateRange: UpdateDateRange;
- setQuery: GlobalTimeArgs['setQuery'];
- skip: boolean;
}
diff --git a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.test.tsx b/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.test.tsx
deleted file mode 100644
index 50fcad8301f1e..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.test.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 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 { TestProviders } from '../../../../../common/mock';
-import { useUsersKpiAuthentications } from '.';
-
-describe('kpi users - authentications', () => {
- it('skip = true will cancel any running request', () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const localProps = {
- startDate: '2020-07-07T08:20:18.966Z',
- endDate: '2020-07-08T08:20:18.966Z',
- indexNames: ['cool'],
- skip: false,
- };
- const { rerender } = renderHook(() => useUsersKpiAuthentications(localProps), {
- wrapper: TestProviders,
- });
- localProps.skip = true;
- act(() => rerender());
- expect(abortSpy).toHaveBeenCalledTimes(4);
- });
-});
diff --git a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.tsx b/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.tsx
deleted file mode 100644
index 29798d69da399..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/index.tsx
+++ /dev/null
@@ -1,167 +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 deepEqual from 'fast-deep-equal';
-import { noop } from 'lodash/fp';
-import { useCallback, useEffect, useRef, useState } from 'react';
-import { Subscription } from 'rxjs';
-
-import type { AuthenticationsKpiRequestOptionsInput } from '../../../../../../common/api/search_strategy';
-import { useAppToasts } from '../../../../../common/hooks/use_app_toasts';
-import type { inputsModel } from '../../../../../common/store';
-import { createFilter } from '../../../../../common/containers/helpers';
-import { useKibana } from '../../../../../common/lib/kibana';
-import type { UsersKpiAuthenticationsStrategyResponse } from '../../../../../../common/search_strategy';
-import { UsersQueries } from '../../../../../../common/search_strategy';
-import type { ESTermQuery } from '../../../../../../common/typed_json';
-
-import * as i18n from './translations';
-import { getInspectResponse } from '../../../../../helpers';
-import type { InspectResponse } from '../../../../../types';
-
-export const ID = 'usersKpiAuthenticationsQuery';
-
-export interface UsersKpiAuthenticationsArgs
- extends Omit {
- id: string;
- inspect: InspectResponse;
- isInspected: boolean;
- refetch: inputsModel.Refetch;
-}
-
-interface UseUsersKpiAuthentications {
- filterQuery?: ESTermQuery | string;
- endDate: string;
- indexNames: string[];
- skip?: boolean;
- startDate: string;
-}
-
-export const useUsersKpiAuthentications = ({
- filterQuery,
- endDate,
- indexNames,
- skip = false,
- startDate,
-}: UseUsersKpiAuthentications): [boolean, UsersKpiAuthenticationsArgs] => {
- const { data } = useKibana().services;
- const refetch = useRef(noop);
- const abortCtrl = useRef(new AbortController());
- const searchSubscription$ = useRef(new Subscription());
- const [loading, setLoading] = useState(false);
- const [usersKpiAuthenticationsRequest, setUsersKpiAuthenticationsRequest] =
- useState(null);
-
- const [usersKpiAuthenticationsResponse, setUsersKpiAuthenticationsResponse] =
- useState({
- authenticationsSuccess: 0,
- authenticationsSuccessHistogram: [],
- authenticationsFailure: 0,
- authenticationsFailureHistogram: [],
- id: ID,
- inspect: {
- dsl: [],
- response: [],
- },
- isInspected: false,
- refetch: refetch.current,
- });
- const { addError, addWarning } = useAppToasts();
-
- const usersKpiAuthenticationsSearch = useCallback(
- (request: AuthenticationsKpiRequestOptionsInput | null) => {
- if (request == null || skip) {
- return;
- }
-
- const asyncSearch = async () => {
- abortCtrl.current = new AbortController();
- setLoading(true);
-
- searchSubscription$.current = data.search
- .search(
- request,
- {
- strategy: 'securitySolutionSearchStrategy',
- abortSignal: abortCtrl.current.signal,
- }
- )
- .subscribe({
- next: (response) => {
- if (!response.isPartial && !response.isRunning) {
- setLoading(false);
- setUsersKpiAuthenticationsResponse((prevResponse) => ({
- ...prevResponse,
- authenticationsSuccess: response.authenticationsSuccess,
- authenticationsSuccessHistogram: response.authenticationsSuccessHistogram,
- authenticationsFailure: response.authenticationsFailure,
- authenticationsFailureHistogram: response.authenticationsFailureHistogram,
- inspect: getInspectResponse(response, prevResponse.inspect),
- refetch: refetch.current,
- }));
- searchSubscription$.current.unsubscribe();
- } else if (response.isPartial && !response.isRunning) {
- setLoading(false);
- addWarning(i18n.ERROR_USERS_KPI_AUTHENTICATIONS);
- searchSubscription$.current.unsubscribe();
- }
- },
- error: (msg) => {
- setLoading(false);
- addError(msg, {
- title: i18n.FAIL_USERS_KPI_AUTHENTICATIONS,
- });
- searchSubscription$.current.unsubscribe();
- },
- });
- };
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- asyncSearch();
- refetch.current = asyncSearch;
- },
- [data.search, addError, addWarning, skip]
- );
-
- useEffect(() => {
- setUsersKpiAuthenticationsRequest((prevRequest) => {
- const myRequest: AuthenticationsKpiRequestOptionsInput = {
- ...(prevRequest ?? {}),
- defaultIndex: indexNames,
- factoryQueryType: UsersQueries.kpiAuthentications,
- filterQuery: createFilter(filterQuery),
- timerange: {
- interval: '12h',
- from: startDate,
- to: endDate,
- },
- };
- if (!deepEqual(prevRequest, myRequest)) {
- return myRequest;
- }
- return prevRequest;
- });
- }, [indexNames, endDate, filterQuery, startDate]);
-
- useEffect(() => {
- usersKpiAuthenticationsSearch(usersKpiAuthenticationsRequest);
- return () => {
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- };
- }, [usersKpiAuthenticationsRequest, usersKpiAuthenticationsSearch]);
-
- useEffect(() => {
- if (skip) {
- setLoading(false);
- searchSubscription$.current.unsubscribe();
- abortCtrl.current.abort();
- }
- }, [skip]);
-
- return [loading, usersKpiAuthenticationsResponse];
-};
diff --git a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/translations.ts b/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/translations.ts
deleted file mode 100644
index 7c434563d384b..0000000000000
--- a/x-pack/plugins/security_solution/public/explore/users/containers/users/authentications/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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export const ERROR_USERS_KPI_AUTHENTICATIONS = i18n.translate(
- 'xpack.securitySolution.usersKpiAuthentications.errorSearchDescription',
- {
- defaultMessage: `An error has occurred on users kpi authentications search`,
- }
-);
-
-export const FAIL_USERS_KPI_AUTHENTICATIONS = i18n.translate(
- 'xpack.securitySolution.usersKpiAuthentications.failSearchDescription',
- {
- defaultMessage: `Failed to run search on users kpi authentications`,
- }
-);
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.tsx
index 296424b759e9d..db286f63d5cd0 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/pages/navigation/authentications_query_tab_body.tsx
@@ -31,7 +31,6 @@ export const AuthenticationsQueryTabBody = ({
endDate={endDate}
filterQuery={filterQuery}
id={HISTOGRAM_QUERY_ID}
- indexNames={indexNames}
setQuery={setQuery}
startDate={startDate}
{...histogramConfigs}
diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx
index fd0f7deff446c..41f974971b0e1 100644
--- a/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx
+++ b/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx
@@ -9,7 +9,6 @@ import { EuiSpacer, EuiWindowEvent } from '@elastic/eui';
import styled from 'styled-components';
import { noop } from 'lodash/fp';
import React, { useCallback, useMemo, useRef } from 'react';
-import { useDispatch } from 'react-redux';
import { useParams } from 'react-router-dom';
import type { Filter } from '@kbn/es-query';
import { isTab } from '@kbn/timelines-plugin/public';
@@ -29,7 +28,6 @@ import { useKibana } from '../../../common/lib/kibana';
import { convertToBuildEsQuery } from '../../../common/lib/kuery';
import type { State } from '../../../common/store';
import { inputsSelectors } from '../../../common/store';
-import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions';
import { SpyRoute } from '../../../common/utils/route/spy_routes';
import { UsersTabs } from './users_tabs';
@@ -44,7 +42,6 @@ import { useSourcererDataView } from '../../../common/containers/sourcerer';
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
import { UsersKpiComponent } from '../components/kpi_users';
-import type { UpdateDateRange } from '../../../common/components/charts/common';
import { LastEventIndexKey, RiskScoreEntity } from '../../../../common/search_strategy';
import { generateSeverityFilter } from '../../hosts/store/helpers';
import { UsersTableType } from '../store/model';
@@ -66,7 +63,6 @@ const StyledFullHeightContainer = styled.div`
`;
const UsersComponent = () => {
- const dispatch = useDispatch();
const containerElement = useRef(null);
const getGlobalFiltersQuerySelector = useMemo(
@@ -159,23 +155,6 @@ const UsersComponent = () => {
[containerElement, onSkipFocusBeforeEventsTable, onSkipFocusAfterEventsTable]
);
- const updateDateRange = useCallback(
- ({ x }) => {
- if (!x) {
- return;
- }
- const [min, max] = x;
- dispatch(
- setAbsoluteRangeDatePicker({
- id: InputsModelId.global,
- from: new Date(min).toISOString(),
- to: new Date(max).toISOString(),
- })
- );
- },
- [dispatch]
- );
-
const capabilities = useMlCapabilities();
const hasEntityAnalyticsCapability = useHasSecurityCapability('entity-analytics');
const navTabs = useMemo(
@@ -201,15 +180,7 @@ const UsersComponent = () => {
title={i18n.PAGE_TITLE}
/>
-
+
diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx
index 6d2c7121b6ad3..68c2b4868a312 100644
--- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx
+++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx
@@ -10,17 +10,26 @@ import { render } from '@testing-library/react';
import React from 'react';
import { HostDetailsPanel } from '.';
import { TestProviders } from '../../../common/mock';
+import type { HostRiskScore } from '../../../../common/search_strategy';
import { RiskSeverity } from '../../../../common/search_strategy';
-const riskScore = {
+const riskScore: HostRiskScore = {
'@timestamp': '2021-08-19T16:00:00.000Z',
host: {
name: 'elastic',
risk: {
+ '@timestamp': '2021-08-19T16:00:00.000Z',
+ id_field: 'host.name',
+ id_value: 'elastic',
rule_risks: [],
calculated_score_norm: 100,
+ calculated_score: 150,
+ category_1_score: 150,
+ category_1_count: 1,
multipliers: [],
calculated_level: RiskSeverity.critical,
+ inputs: [],
+ notes: [],
},
},
};
diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status_visualization_data.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status_visualization_data.ts
index 218d69b4183a7..441528836358a 100644
--- a/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status_visualization_data.ts
+++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/alerts_by_status/use_alerts_by_status_visualization_data.ts
@@ -13,24 +13,24 @@ export const acknowledgedAlertsVisualizationId = `${DETECTION_RESPONSE_ALERTS_BY
export const closedAlertsVisualizationId = `${DETECTION_RESPONSE_ALERTS_BY_STATUS_ID}-closed`;
export const useAlertsByStatusVisualizationData = () => {
- const { responses: openAlertsResponse } = useVisualizationResponse({
+ const { responses: openAlertsResponses } = useVisualizationResponse({
visualizationId: openAlertsVisualizationId,
});
- const { responses: acknowledgedAlertsResponse } = useVisualizationResponse({
+ const { responses: acknowledgedAlertsResponses } = useVisualizationResponse({
visualizationId: acknowledgedAlertsVisualizationId,
});
- const { responses: closedAlertsResponse } = useVisualizationResponse({
+ const { responses: closedAlertsResponses } = useVisualizationResponse({
visualizationId: closedAlertsVisualizationId,
});
const visualizationOpenAlertsData =
- openAlertsResponse != null ? openAlertsResponse[0].hits.total : 0;
+ openAlertsResponses != null ? openAlertsResponses[0].hits.total : 0;
const visualizationAcknowledgedAlertsData =
- acknowledgedAlertsResponse != null ? acknowledgedAlertsResponse[0].hits.total : 0;
+ acknowledgedAlertsResponses != null ? acknowledgedAlertsResponses[0].hits.total : 0;
const visualizationClosedAlertsData =
- closedAlertsResponse != null ? closedAlertsResponse[0].hits.total : 0;
+ closedAlertsResponses != null ? closedAlertsResponses[0].hits.total : 0;
const visualizationTotalAlertsData =
visualizationOpenAlertsData +
diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx
index 9a62b29aa411c..5d673674225fd 100644
--- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx
@@ -14,7 +14,6 @@ import type { DataViewBase, Filter, Query } from '@kbn/es-query';
import styled from 'styled-components';
import { EuiButton } from '@elastic/eui';
import { getEsQueryConfig } from '@kbn/data-plugin/common';
-import type { RunTimeMappings } from '@kbn/timelines-plugin/common/api/search_strategy';
import { DEFAULT_NUMBER_FORMAT, APP_UI_ID } from '../../../../common/constants';
import { SHOWING, UNIT } from '../../../common/components/events_viewer/translations';
import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts';
@@ -196,17 +195,10 @@ const EventsByDatasetComponent: React.FC = ({
filterQuery={filterQuery}
headerChildren={headerContent}
id={uniqueQueryId}
- indexNames={indexNames}
- runtimeMappings={runtimeMappings as RunTimeMappings}
- onError={toggleTopN}
paddingSize={paddingSize}
- setAbsoluteRangeDatePickerTarget={setAbsoluteRangeDatePickerTarget}
setQuery={setQuery}
showSpacer={showSpacer}
- showLegend={showLegend}
- skip={filterQuery === undefined}
startDate={from}
- scopeId={scopeId}
sourcererScopeId={sourcererScopeId}
{...eventsByDatasetHistogramConfigs}
title={onlyField != null ? i18n.TOP(onlyField) : eventsByDatasetHistogramConfigs.title}
diff --git a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx
index f03058683f2a7..f25ef25081d05 100644
--- a/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/signals_by_category/signals_by_category.tsx
@@ -7,8 +7,7 @@
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
-import type { Filter, Query } from '@kbn/es-query';
-import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
+import type { Filter } from '@kbn/es-query';
import { AlertsHistogramPanel } from '../../../detections/components/alerts_kpis/alerts_histogram_panel';
import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index';
@@ -24,29 +23,21 @@ import * as i18n from '../../pages/translations';
import { useFiltersForSignalsByCategory } from './use_filters_for_signals_by_category';
interface Props {
- combinedQueries?: string;
filters: Filter[];
headerChildren?: React.ReactNode;
/** Override all defaults, and only display this field */
onlyField?: AlertsStackByField;
paddingSize?: 's' | 'm' | 'l' | 'none';
- query?: Query;
setAbsoluteRangeDatePickerTarget?: InputsModelId;
- showLegend?: boolean;
- runtimeMappings?: MappingRuntimeFields;
hideQueryToggle?: boolean;
}
const SignalsByCategoryComponent: React.FC = ({
- combinedQueries,
filters,
headerChildren,
onlyField,
paddingSize,
- query,
- showLegend,
setAbsoluteRangeDatePickerTarget = InputsModelId.global,
- runtimeMappings,
hideQueryToggle = false,
}) => {
const dispatch = useDispatch();
@@ -72,19 +63,14 @@ const SignalsByCategoryComponent: React.FC = ({
return (
{
{hasIndexRead && hasKibanaREAD && (
-
+
)}
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts
index 894277d5ad17f..1dc4d92b44ab9 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/threat_mapping/create_threat_signals.ts
@@ -30,7 +30,7 @@ import { getAllowedFieldsForTermQuery } from './get_allowed_fields_for_terms_que
import { getEventCount, getEventList } from './get_event_count';
import { getMappingFilters } from './get_mapping_filters';
import { THREAT_PIT_KEEP_ALIVE } from '../../../../../../common/cti/constants';
-import { getMaxSignalsWarning } from '../../utils/utils';
+import { getMaxSignalsWarning, getSafeSortIds } from '../../utils/utils';
import { getFieldsForWildcard } from '../../utils/get_fields_for_wildcard';
export const createThreatSignals = async ({
@@ -213,8 +213,24 @@ export const createThreatSignals = async ({
}
ruleExecutionLogger.debug(`Documents items left to check are ${documentCount}`);
+ const sortIds = getSafeSortIds(list.hits.hits[list.hits.hits.length - 1].sort);
+
+ // ES can return negative sort id for date field, when sort order set to desc
+ // this could happen when event has empty sort field
+ // https://github.com/elastic/kibana/issues/174573 (happens to IM rule only since it uses desc order for events search)
+ // when negative sort id used in subsequent request it fails, so when negative sort value found we don't do next request
+ const hasNegativeDateSort = sortIds?.some((val) => val < 0);
+
+ if (hasNegativeDateSort) {
+ ruleExecutionLogger.debug(
+ `Negative date sort id value encountered: ${sortIds}. Threat search stopped.`
+ );
+
+ break;
+ }
+
list = await getDocumentList({
- searchAfter: list.hits.hits[list.hits.hits.length - 1].sort,
+ searchAfter: sortIds,
});
}
};
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create_factory.ts
index cdbb0530a0452..1706f655d259b 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create_factory.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/utils/search_after_bulk_create_factory.ts
@@ -140,7 +140,13 @@ export const searchAfterAndBulkCreateFactory = async ({
);
}
- if (lastSortIds != null && lastSortIds.length !== 0) {
+ // ES can return negative sort id for date field, when sort order set to desc
+ // this could happen when event has empty sort field
+ // https://github.com/elastic/kibana/issues/174573 (happens to IM rule only since it uses desc order for events search)
+ // when negative sort id used in subsequent request it fails, so when negative sort value found we don't do next request
+ const hasNegativeNumber = lastSortIds?.some((val) => val < 0);
+
+ if (lastSortIds != null && lastSortIds.length !== 0 && !hasNegativeNumber) {
sortIds = lastSortIds;
hasSortId = true;
} else {
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts
index 65220ec90a715..596e371fad4fb 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/delete.ts
@@ -12,13 +12,13 @@ import {
APP_ID,
ENABLE_ASSET_CRITICALITY_SETTING,
} from '../../../../../common/constants';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { AssetCriticalityRecordIdParts } from '../../../../../common/api/entity_analytics/asset_criticality';
import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation';
import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources';
import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
export const assetCriticalityDeleteRoute = (
- router: SecuritySolutionPluginRouter,
+ router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts
index 5ed7752bf6786..b81eddfa5ecea 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts
@@ -12,12 +12,15 @@ import {
APP_ID,
ENABLE_ASSET_CRITICALITY_SETTING,
} from '../../../../../common/constants';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources';
import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation';
import { AssetCriticalityRecordIdParts } from '../../../../../common/api/entity_analytics/asset_criticality';
import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled';
-export const assetCriticalityGetRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {
+import type { EntityAnalyticsRoutesDeps } from '../../types';
+export const assetCriticalityGetRoute = (
+ router: EntityAnalyticsRoutesDeps['router'],
+ logger: Logger
+) => {
router.versioned
.get({
access: 'internal',
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts
index d74050e3436a1..68115fdf255b2 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts
@@ -5,9 +5,4 @@
* 2.0.
*/
-export { assetCriticalityStatusRoute } from './status';
-export { assetCriticalityUpsertRoute } from './upsert';
-export { assetCriticalityGetRoute } from './get';
-export { assetCriticalityDeleteRoute } from './delete';
-export { assetCriticalityPrivilegesRoute } from './privileges';
-export { assetCriticalityCSVUploadRoute } from './upload_csv';
+export { registerAssetCriticalityRoutes } from './register_asset_criticality_routes';
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts
index 67157c8b4e57e..a4433eaaca4cf 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts
@@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import type { Logger, StartServicesAccessor } from '@kbn/core/server';
+import type { Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import {
@@ -12,16 +12,15 @@ import {
APP_ID,
ENABLE_ASSET_CRITICALITY_SETTING,
} from '../../../../../common/constants';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources';
import { getUserAssetCriticalityPrivileges } from '../get_user_asset_criticality_privileges';
-
-import type { StartPlugins } from '../../../../plugin';
import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
+
export const assetCriticalityPrivilegesRoute = (
- router: SecuritySolutionPluginRouter,
- getStartServices: StartServicesAccessor,
- logger: Logger
+ router: EntityAnalyticsRoutesDeps['router'],
+ logger: Logger,
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
router.versioned
.get({
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/register_asset_criticality_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/register_asset_criticality_routes.ts
new file mode 100644
index 0000000000000..518602a4bc9b7
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/register_asset_criticality_routes.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import { assetCriticalityStatusRoute } from './status';
+import { assetCriticalityUpsertRoute } from './upsert';
+import { assetCriticalityGetRoute } from './get';
+import { assetCriticalityDeleteRoute } from './delete';
+import { assetCriticalityPrivilegesRoute } from './privileges';
+import { assetCriticalityCSVUploadRoute } from './upload_csv';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
+
+export const registerAssetCriticalityRoutes = ({
+ router,
+ logger,
+ config,
+ getStartServices,
+}: EntityAnalyticsRoutesDeps) => {
+ assetCriticalityStatusRoute(router, logger);
+ assetCriticalityUpsertRoute(router, logger);
+ assetCriticalityGetRoute(router, logger);
+ assetCriticalityDeleteRoute(router, logger);
+ assetCriticalityPrivilegesRoute(router, logger, getStartServices);
+ assetCriticalityCSVUploadRoute(router, logger, config, getStartServices);
+};
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts
index f660295e20390..465cfcc4d4154 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/status.ts
@@ -13,12 +13,12 @@ import {
APP_ID,
ENABLE_ASSET_CRITICALITY_SETTING,
} from '../../../../../common/constants';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled';
import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources';
export const assetCriticalityStatusRoute = (
- router: SecuritySolutionPluginRouter,
+ router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts
index 4a9994fe9b1e1..2374c4427b6d3 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upload_csv.ts
@@ -4,16 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
-import type { Logger, StartServicesAccessor } from '@kbn/core/server';
+import type { Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { schema } from '@kbn/config-schema';
import Papa from 'papaparse';
import { transformError } from '@kbn/securitysolution-es-utils';
-import type { StartPlugins } from '../../../../plugin';
import type { AssetCriticalityCsvUploadResponse } from '../../../../../common/api/entity_analytics';
import { CRITICALITY_CSV_MAX_SIZE_BYTES_WITH_TOLERANCE } from '../../../../../common/entity_analytics/asset_criticality';
import type { ConfigType } from '../../../../config';
-import type { HapiReadableStream, SecuritySolutionPluginRouter } from '../../../../types';
+import type { HapiReadableStream } from '../../../../types';
import {
ASSET_CRITICALITY_CSV_UPLOAD_URL,
APP_ID,
@@ -23,12 +22,13 @@ import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_c
import { transformCSVToUpsertRecords } from '../transform_csv_to_upsert_records';
import { createAssetCriticalityProcessedFileEvent } from '../../../telemetry/event_based/events';
import { assertAdvancedSettingsEnabled } from '../../utils/assert_advanced_setting_enabled';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
export const assetCriticalityCSVUploadRoute = (
- router: SecuritySolutionPluginRouter,
+ router: EntityAnalyticsRoutesDeps['router'],
logger: Logger,
config: ConfigType,
- getStartServices: StartServicesAccessor
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
const { errorRetries, maxBulkRequestBodySizeBytes } =
config.entityAnalytics.assetCriticality.csvUpload;
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts
index 65f71bb3bfe45..c1e9db429ceaa 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/upsert.ts
@@ -8,12 +8,12 @@ import type { Logger } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { ASSET_CRITICALITY_URL, APP_ID } from '../../../../../common/constants';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources';
import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation';
import { CreateAssetCriticalityRecord } from '../../../../../common/api/entity_analytics/asset_criticality';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
export const assetCriticalityUpsertRoute = (
- router: SecuritySolutionPluginRouter,
+ router: EntityAnalyticsRoutesDeps['router'],
logger: Logger
) => {
router.versioned
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.ts
new file mode 100644
index 0000000000000..31a7ccbb6f30c
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/register_entity_analytics_routes.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 { registerAssetCriticalityRoutes } from './asset_criticality/routes';
+import { registerRiskScoreRoutes } from './risk_score/routes';
+import { registerRiskEngineRoutes } from './risk_engine/routes';
+import type { EntityAnalyticsRoutesDeps } from './types';
+
+export const registerEntityAnalyticsRoutes = (routeDeps: EntityAnalyticsRoutesDeps) => {
+ registerAssetCriticalityRoutes(routeDeps);
+ registerRiskScoreRoutes(routeDeps);
+ registerRiskEngineRoutes(routeDeps);
+};
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts
index 4f9ffc99057be..89fbdd730c912 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts
@@ -5,18 +5,16 @@
* 2.0.
*/
-import type { StartServicesAccessor } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RISK_ENGINE_DISABLE_URL, APP_ID } from '../../../../../common/constants';
-import type { StartPlugins } from '../../../../plugin';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations';
import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
export const riskEngineDisableRoute = (
- router: SecuritySolutionPluginRouter,
- getStartServices: StartServicesAccessor
+ router: EntityAnalyticsRoutesDeps['router'],
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
router.versioned
.post({
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts
index e63a914efadb9..a086278a8b7d2 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts
@@ -5,18 +5,16 @@
* 2.0.
*/
-import type { StartServicesAccessor } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RISK_ENGINE_ENABLE_URL, APP_ID } from '../../../../../common/constants';
import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations';
-import type { StartPlugins } from '../../../../plugin';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
export const riskEngineEnableRoute = (
- router: SecuritySolutionPluginRouter,
- getStartServices: StartServicesAccessor
+ router: EntityAnalyticsRoutesDeps['router'],
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
router.versioned
.post({
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts
index df74b846ed464..fd124f9307455 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/index.ts
@@ -5,9 +5,4 @@
* 2.0.
*/
-export { riskEngineInitRoute } from './init';
-export { riskEngineEnableRoute } from './enable';
-export { riskEngineDisableRoute } from './disable';
-export { riskEngineStatusRoute } from './status';
-export { riskEnginePrivilegesRoute } from './privileges';
-export { riskEngineSettingsRoute } from './settings';
+export { registerRiskEngineRoutes } from './register_risk_engine_routes';
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts
index ffc6608505804..5dcc657497fc0 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts
@@ -5,18 +5,15 @@
* 2.0.
*/
-import type { StartServicesAccessor } from '@kbn/core/server';
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RISK_ENGINE_INIT_URL, APP_ID } from '../../../../../common/constants';
-import type { StartPlugins } from '../../../../plugin';
import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
-import type { InitRiskEngineResultResponse } from '../../types';
+import type { EntityAnalyticsRoutesDeps, InitRiskEngineResultResponse } from '../../types';
import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges';
export const riskEngineInitRoute = (
- router: SecuritySolutionPluginRouter,
- getStartServices: StartServicesAccessor
+ router: EntityAnalyticsRoutesDeps['router'],
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
router.versioned
.post({
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts
index 62b9bf4584aed..ebcf382cc148e 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts
@@ -7,16 +7,14 @@
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
-import type { StartServicesAccessor } from '@kbn/core/server';
import { RISK_ENGINE_PRIVILEGES_URL, APP_ID } from '../../../../../common/constants';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
-import type { StartPlugins } from '../../../../plugin';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { getUserRiskEnginePrivileges } from '../risk_engine_privileges';
export const riskEnginePrivilegesRoute = (
- router: SecuritySolutionPluginRouter,
- getStartServices: StartServicesAccessor
+ router: EntityAnalyticsRoutesDeps['router'],
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices']
) => {
router.versioned
.get({
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts
new file mode 100644
index 0000000000000..73e4370a26285
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.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 { riskEngineInitRoute } from './init';
+import { riskEngineEnableRoute } from './enable';
+import { riskEngineDisableRoute } from './disable';
+import { riskEngineStatusRoute } from './status';
+import { riskEnginePrivilegesRoute } from './privileges';
+import { riskEngineSettingsRoute } from './settings';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
+
+export const registerRiskEngineRoutes = ({
+ router,
+ getStartServices,
+}: EntityAnalyticsRoutesDeps) => {
+ riskEngineStatusRoute(router);
+ riskEngineInitRoute(router, getStartServices);
+ riskEngineEnableRoute(router, getStartServices);
+ riskEngineDisableRoute(router, getStartServices);
+ riskEngineSettingsRoute(router);
+ riskEnginePrivilegesRoute(router, getStartServices);
+};
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts
index 50228a5542bd9..e2af681785554 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts
@@ -8,10 +8,9 @@
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RISK_ENGINE_SETTINGS_URL, APP_ID } from '../../../../../common/constants';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
-
-export const riskEngineSettingsRoute = (router: SecuritySolutionPluginRouter) => {
+export const riskEngineSettingsRoute = (router: EntityAnalyticsRoutesDeps['router']) => {
router.versioned
.get({
access: 'internal',
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts
index a7d649c0d7784..46384eb776aec 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts
@@ -8,10 +8,9 @@
import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils';
import { transformError } from '@kbn/securitysolution-es-utils';
import { RISK_ENGINE_STATUS_URL, APP_ID } from '../../../../../common/constants';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
-
-export const riskEngineStatusRoute = (router: SecuritySolutionPluginRouter) => {
+export const riskEngineStatusRoute = (router: EntityAnalyticsRoutesDeps['router']) => {
router.versioned
.get({
access: 'internal',
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts
index f05d13edc825c..4789ecffe1f7e 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts
@@ -5,10 +5,6 @@
* 2.0.
*/
-import {
- ALERT_RISK_SCORE,
- ALERT_RULE_NAME,
-} from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names';
import { RiskCategories, RiskLevels } from '../../../../common/entity_analytics/risk_engine';
import type { RiskScore } from '../../../../common/entity_analytics/risk_engine';
import type {
@@ -28,36 +24,19 @@ const buildRiskScoreBucketMock = (overrides: Partial = {}): Ris
notes: [],
category_1_score: 30,
category_1_count: 1,
- },
- },
- inputs: {
- took: 17,
- timed_out: false,
- _shards: {
- total: 1,
- successful: 1,
- skipped: 0,
- failed: 0,
- },
- hits: {
- total: {
- value: 1,
- relation: 'eq',
- },
- hits: [
+ risk_inputs: [
{
- _id: '_id',
- _index: '_index',
- fields: {
- '@timestamp': ['2023-07-20T20:31:24.896Z'],
- [ALERT_RISK_SCORE]: [21],
- [ALERT_RULE_NAME]: ['Rule Name'],
- },
- sort: [21],
+ id: 'test_id',
+ index: '_index',
+ rule_name: 'Test rule',
+ time: '2021-08-19T18:55:59.000Z',
+ score: 30,
+ contribution: 20,
},
],
},
},
+
doc_count: 2,
},
...overrides,
@@ -108,6 +87,7 @@ const buildResponseMock = (
description: 'Alert from Rule: My rule',
risk_score: 30,
timestamp: '2021-08-19T18:55:59.000Z',
+ contribution_score: 20,
},
],
},
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts
index 00e6f582367b1..86d0d3cfd2294 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts
@@ -14,6 +14,7 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server';
import {
ALERT_RISK_SCORE,
ALERT_RULE_NAME,
+ ALERT_UUID,
ALERT_WORKFLOW_STATUS,
EVENT_KIND,
} from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names';
@@ -48,6 +49,7 @@ import type {
RiskScoreBucket,
} from '../types';
import {
+ MAX_INPUTS_COUNT,
RISK_SCORING_INPUTS_COUNT_MAX,
RISK_SCORING_SUM_MAX,
RISK_SCORING_SUM_VALUE,
@@ -67,7 +69,6 @@ const formatForResponse = ({
includeNewFields: boolean;
}): RiskScore => {
const riskDetails = bucket.top_inputs.risk_details;
- const inputs = bucket.top_inputs.inputs;
const criticalityModifier = getCriticalityModifier(criticality?.criticality_level);
const normalizedScoreWithCriticality = applyCriticalityToScore({
@@ -98,15 +99,14 @@ const formatForResponse = ({
}),
category_1_count: riskDetails.value.category_1_count,
notes: riskDetails.value.notes,
- inputs: inputs.hits.hits.map((riskInput) => ({
- id: riskInput._id,
- index: riskInput._index,
- description: `Alert from Rule: ${
- riskInput.fields?.[ALERT_RULE_NAME]?.[0] ?? 'RULE_NOT_FOUND'
- }`,
+ inputs: riskDetails.value.risk_inputs.map((riskInput) => ({
+ id: riskInput.id,
+ index: riskInput.index,
+ description: `Alert from Rule: ${riskInput.rule_name ?? 'RULE_NOT_FOUND'}`,
category: RiskCategories.category_1,
- risk_score: riskInput.fields?.[ALERT_RISK_SCORE]?.[0] ?? undefined,
- timestamp: riskInput.fields?.['@timestamp']?.[0] ?? undefined,
+ risk_score: riskInput.score,
+ timestamp: riskInput.time,
+ contribution_score: riskInput.contribution,
})),
...(includeNewFields ? newFields : {}),
};
@@ -140,9 +140,15 @@ const buildReduceScript = ({
double total_score = 0;
double current_score = 0;
+ List risk_inputs = [];
for (int i = 0; i < num_inputs_to_score; i++) {
current_score = inputs[i].weighted_score / Math.pow(i + 1, params.p);
+ if (i < ${MAX_INPUTS_COUNT}) {
+ inputs[i]["contribution"] = 100 * current_score / params.risk_cap;
+ risk_inputs.add(inputs[i]);
+ }
+
${buildCategoryAssignment()}
total_score += current_score;
}
@@ -151,6 +157,7 @@ const buildReduceScript = ({
double score_norm = 100 * total_score / params.risk_cap;
results['score'] = total_score;
results['normalized_score'] = score_norm;
+ results['risk_inputs'] = risk_inputs;
return results;
`;
@@ -191,14 +198,8 @@ const buildIdentifierTypeAggregation = ({
sampler: {
shard_size: alertSampleSizePerShard,
},
+
aggs: {
- inputs: {
- top_hits: {
- size: 5,
- _source: false,
- docvalue_fields: ['@timestamp', ALERT_RISK_SCORE, ALERT_RULE_NAME],
- },
- },
risk_details: {
scripted_metric: {
init_script: 'state.inputs = []',
@@ -209,8 +210,13 @@ const buildIdentifierTypeAggregation = ({
double weighted_score = 0.0;
fields.put('time', doc['@timestamp'].value);
+ fields.put('rule_name', doc['${ALERT_RULE_NAME}'].value);
+
fields.put('category', category);
+ fields.put('index', doc['_index'].value);
+ fields.put('id', doc['${ALERT_UUID}'].value);
fields.put('score', score);
+
${buildWeightingOfScoreByCategory({ userWeights: weights, identifierType })}
fields.put('weighted_score', weighted_score);
@@ -308,7 +314,6 @@ export const calculateRiskScores = async ({
filter.push(userFilter as QueryDslQueryContainer);
}
const identifierTypes: IdentifierType[] = identifierType ? [identifierType] : ['host', 'user'];
-
const request = {
size: 0,
_source: false,
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts
index 57e67960f96e2..73f93d71b11f9 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/constants.ts
@@ -25,3 +25,8 @@ export const RISK_SCORING_INPUTS_COUNT_MAX = 999999;
* This value represents the maximum possible risk score after normalization.
*/
export const RISK_SCORING_NORMALIZATION_MAX = 100;
+
+/**
+ * This value represents the max amount of alert inputs we store, per entity, in the risk document.
+ */
+export const MAX_INPUTS_COUNT = 10;
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts
index efa37e155595b..e5a489852c48e 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts
@@ -14,13 +14,16 @@ import {
RISK_SCORE_CALCULATION_URL,
} from '../../../../../common/constants';
import { riskScoreCalculationRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_calculation/request_schema';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
import { assetCriticalityServiceFactory } from '../../asset_criticality';
import { riskScoreServiceFactory } from '../risk_score_service';
import { getRiskInputsIndex } from '../get_risk_inputs_index';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
-export const riskScoreCalculationRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {
+export const riskScoreCalculationRoute = (
+ router: EntityAnalyticsRoutesDeps['router'],
+ logger: Logger
+) => {
router.versioned
.post({
path: RISK_SCORE_CALCULATION_URL,
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts
index 56d769899641d..9aa76a2a4c71f 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/index.ts
@@ -5,4 +5,4 @@
* 2.0.
*/
-export { riskScorePreviewRoute } from './preview';
+export { registerRiskScoreRoutes } from './register_risk_score_routes';
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts
index b4026b0b3c761..86bfa37f219f0 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts
@@ -15,13 +15,16 @@ import {
RISK_SCORE_PREVIEW_URL,
} from '../../../../../common/constants';
import { riskScorePreviewRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema';
-import type { SecuritySolutionPluginRouter } from '../../../../types';
import { buildRouteValidation } from '../../../../utils/build_validation/route_validation';
import { assetCriticalityServiceFactory } from '../../asset_criticality';
import { riskScoreServiceFactory } from '../risk_score_service';
import { getRiskInputsIndex } from '../get_risk_inputs_index';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
-export const riskScorePreviewRoute = (router: SecuritySolutionPluginRouter, logger: Logger) => {
+export const riskScorePreviewRoute = (
+ router: EntityAnalyticsRoutesDeps['router'],
+ logger: Logger
+) => {
router.versioned
.post({
access: 'internal',
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/register_risk_score_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/register_risk_score_routes.ts
new file mode 100644
index 0000000000000..28cab1cdca397
--- /dev/null
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/register_risk_score_routes.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 { riskScorePreviewRoute } from './preview';
+import { riskScoreCalculationRoute } from './calculation';
+import type { EntityAnalyticsRoutesDeps } from '../../types';
+
+export const registerRiskScoreRoutes = ({ router, logger }: EntityAnalyticsRoutesDeps) => {
+ riskScorePreviewRoute(router, logger);
+ riskScoreCalculationRoute(router, logger);
+};
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts
index c3b5674fc38a2..5e1881cb7173f 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts
@@ -7,11 +7,7 @@
import moment from 'moment';
import { asyncForEach } from '@kbn/std';
-import {
- type Logger,
- SavedObjectsErrorHelpers,
- type StartServicesAccessor,
-} from '@kbn/core/server';
+import { type Logger, SavedObjectsErrorHelpers } from '@kbn/core/server';
import type {
ConcreteTaskInstance,
TaskManagerSetupContract,
@@ -23,7 +19,6 @@ import {
type IdentifierType,
RiskScoreEntity,
} from '../../../../../common/entity_analytics/risk_engine';
-import type { StartPlugins } from '../../../../plugin';
import { type RiskScoreService, riskScoreServiceFactory } from '../risk_score_service';
import { RiskEngineDataClient } from '../../risk_engine/risk_engine_data_client';
import { RiskScoreDataClient } from '../risk_score_data_client';
@@ -44,7 +39,7 @@ import {
AssetCriticalityDataClient,
assetCriticalityServiceFactory,
} from '../../asset_criticality';
-import type { EntityAnalyticsConfig } from '../../types';
+import type { EntityAnalyticsConfig, EntityAnalyticsRoutesDeps } from '../../types';
const logFactory =
(logger: Logger, taskId: string) =>
@@ -65,7 +60,7 @@ export const registerRiskScoringTask = ({
telemetry,
entityAnalyticsConfig,
}: {
- getStartServices: StartServicesAccessor;
+ getStartServices: EntityAnalyticsRoutesDeps['getStartServices'];
kibanaVersion: string;
logger: Logger;
taskManager: TaskManagerSetupContract | undefined;
diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts
index eb989716153ca..c5a65cb59c5e3 100644
--- a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts
+++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts
@@ -5,7 +5,8 @@
* 2.0.
*/
-import type { MappingRuntimeFields, SearchResponse } from '@elastic/elasticsearch/lib/api/types';
+import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types';
+import type { Logger, StartServicesAccessor } from '@kbn/core/server';
import type {
AfterKey,
AfterKeys,
@@ -16,8 +17,16 @@ import type {
RiskScore,
} from '../../../common/entity_analytics/risk_engine';
import type { ConfigType } from '../../config';
+import type { StartPlugins } from '../../plugin';
+import type { SecuritySolutionPluginRouter } from '../../types';
export type EntityAnalyticsConfig = ConfigType['entityAnalytics'];
+export interface EntityAnalyticsRoutesDeps {
+ router: SecuritySolutionPluginRouter;
+ logger: Logger;
+ config: ConfigType;
+ getStartServices: StartServicesAccessor;
+}
export interface CalculateScoresParams {
afterKeys: AfterKeys;
debug?: boolean;
@@ -113,6 +122,15 @@ export interface CalculateRiskScoreAggregations {
};
}
+export interface SearchHitRiskInput {
+ id: string;
+ index: string;
+ rule_name?: string;
+ time?: string;
+ score?: number;
+ contribution?: number;
+}
+
export interface RiskScoreBucket {
key: { [identifierField: string]: string };
doc_count: number;
@@ -125,9 +143,9 @@ export interface RiskScoreBucket {
notes: string[];
category_1_score: number;
category_1_count: number;
+ risk_inputs: SearchHitRiskInput[];
};
};
- inputs: SearchResponse;
};
}
diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts
index bfaae4b5565ee..4365eb4bcc3fa 100644
--- a/x-pack/plugins/security_solution/server/routes/index.ts
+++ b/x-pack/plugins/security_solution/server/routes/index.ts
@@ -58,26 +58,9 @@ import { registerTagsRoutes } from '../lib/tags/routes';
import { setAlertTagsRoute } from '../lib/detection_engine/routes/signals/set_alert_tags_route';
import { setAlertAssigneesRoute } from '../lib/detection_engine/routes/signals/set_alert_assignees_route';
import { suggestUserProfilesRoute } from '../lib/detection_engine/routes/users/suggest_user_profiles_route';
-import {
- riskEngineDisableRoute,
- riskEngineInitRoute,
- riskEngineEnableRoute,
- riskEngineStatusRoute,
- riskEnginePrivilegesRoute,
- riskEngineSettingsRoute,
-} from '../lib/entity_analytics/risk_engine/routes';
import { registerTimelineRoutes } from '../lib/timeline/routes';
-import { riskScoreCalculationRoute } from '../lib/entity_analytics/risk_score/routes/calculation';
-import { riskScorePreviewRoute } from '../lib/entity_analytics/risk_score/routes/preview';
-import {
- assetCriticalityStatusRoute,
- assetCriticalityUpsertRoute,
- assetCriticalityGetRoute,
- assetCriticalityDeleteRoute,
- assetCriticalityPrivilegesRoute,
- assetCriticalityCSVUploadRoute,
-} from '../lib/entity_analytics/asset_criticality/routes';
import { getFleetManagedIndexTemplatesRoute } from '../lib/security_integrations/cribl/routes';
+import { registerEntityAnalyticsRoutes } from '../lib/entity_analytics/register_entity_analytics_routes';
export const initRoutes = (
router: SecuritySolutionPluginRouter,
@@ -161,26 +144,7 @@ export const initRoutes = (
telemetryDetectionRulesPreviewRoute(router, logger, previewTelemetryReceiver, telemetrySender);
}
- if (config.experimentalFeatures.riskScoringRoutesEnabled) {
- riskScorePreviewRoute(router, logger);
- riskScoreCalculationRoute(router, logger);
- riskEngineStatusRoute(router);
- riskEngineInitRoute(router, getStartServices);
- riskEngineEnableRoute(router, getStartServices);
- riskEngineDisableRoute(router, getStartServices);
- riskEngineSettingsRoute(router);
- if (config.experimentalFeatures.riskEnginePrivilegesRouteEnabled) {
- riskEnginePrivilegesRoute(router, getStartServices);
- }
- }
-
- assetCriticalityStatusRoute(router, logger);
- assetCriticalityUpsertRoute(router, logger);
- assetCriticalityGetRoute(router, logger);
- assetCriticalityDeleteRoute(router, logger);
- assetCriticalityPrivilegesRoute(router, getStartServices, logger);
- assetCriticalityCSVUploadRoute(router, logger, config, getStartServices);
-
+ registerEntityAnalyticsRoutes({ router, config, getStartServices, logger });
// Security Integrations
getFleetManagedIndexTemplatesRoute(router);
};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.test.ts
deleted file mode 100644
index a646a73079704..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.test.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { KpiGeneralHistogramCount, KpiHistogram } from '../../../../../common/search_strategy';
-import { formatGeneralHistogramData } from './format_general_histogram_data';
-
-describe('formatGeneralHistogramData', () => {
- test('Picks up data from count.value', () => {
- const mockHistogramData = [
- {
- key_as_string: '2022-12-01T00:00:00.000Z',
- key: 1669852800000,
- doc_count: 4,
- count: {
- doc_count: 4,
- },
- } as KpiHistogram,
- ];
- const result = formatGeneralHistogramData(mockHistogramData);
-
- expect(result).toMatchInlineSnapshot(`
- Array [
- Object {
- "x": 1669852800000,
- "y": 4,
- },
- ]
- `);
- });
-
- test('Picks up data from count.doc_count - userAuthentications', () => {
- const mockUserAuthentications = [
- {
- key_as_string: '2022-12-01T04:00:00.000Z',
- key: 1669867200000,
- doc_count: 4,
- count: {
- value: 1,
- },
- },
- ];
- const result = formatGeneralHistogramData(mockUserAuthentications);
-
- expect(result).toMatchInlineSnapshot(`
- Array [
- Object {
- "x": 1669867200000,
- "y": 1,
- },
- ]
- `);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.ts
deleted file mode 100644
index c52db5b74b8fd..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/common/format_general_histogram_data.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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type {
- KpiHistogramData,
- KpiGeneralHistogramCount,
- KpiHistogram,
-} from '../../../../../common/search_strategy';
-
-export const formatGeneralHistogramData = (
- data: Array>
-): KpiHistogramData[] | null =>
- data && data.length > 0
- ? data.map(({ key, count }) => ({
- x: key,
- y: count.doc_count ?? count.value,
- }))
- : null;
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts
index 193bc524d84fa..130da9b5baf57 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.test.ts
@@ -6,21 +6,17 @@
*/
import { hostsFactory } from '.';
-import { HostsQueries, HostsKpiQueries } from '../../../../../common/search_strategy';
+import { HostsQueries } from '../../../../../common/search_strategy';
import { allHosts } from './all';
import { hostDetails } from './details';
import { hostOverview } from './overview';
import { uncommonProcesses } from './uncommon_processes';
-import { hostsKpiHosts } from './kpi/hosts';
-import { hostsKpiUniqueIps } from './kpi/unique_ips';
jest.mock('./all');
jest.mock('./details');
jest.mock('./overview');
jest.mock('./uncommon_processes');
-jest.mock('./kpi/hosts');
-jest.mock('./kpi/unique_ips');
describe('hostsFactory', () => {
test('should include correct apis', () => {
@@ -29,8 +25,6 @@ describe('hostsFactory', () => {
[HostsQueries.hosts]: allHosts,
[HostsQueries.overview]: hostOverview,
[HostsQueries.uncommonProcesses]: uncommonProcesses,
- [HostsKpiQueries.kpiHosts]: hostsKpiHosts,
- [HostsKpiQueries.kpiUniqueIps]: hostsKpiUniqueIps,
};
expect(hostsFactory).toEqual(expectedHostsFactory);
});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts
index d776754eab064..fd1394add6031 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/index.ts
@@ -6,27 +6,17 @@
*/
import type { FactoryQueryTypes } from '../../../../../common/search_strategy/security_solution';
-import {
- HostsQueries,
- HostsKpiQueries,
-} from '../../../../../common/search_strategy/security_solution';
+import { HostsQueries } from '../../../../../common/search_strategy/security_solution';
import type { SecuritySolutionFactory } from '../types';
import { allHosts } from './all';
import { hostDetails } from './details';
import { hostOverview } from './overview';
import { uncommonProcesses } from './uncommon_processes';
-import { hostsKpiHosts } from './kpi/hosts';
-import { hostsKpiUniqueIps } from './kpi/unique_ips';
-export const hostsFactory: Record<
- HostsQueries | HostsKpiQueries,
- SecuritySolutionFactory
-> = {
+export const hostsFactory: Record> = {
[HostsQueries.details]: hostDetails,
[HostsQueries.hosts]: allHosts,
[HostsQueries.overview]: hostOverview,
[HostsQueries.uncommonProcesses]: uncommonProcesses,
- [HostsKpiQueries.kpiHosts]: hostsKpiHosts,
- [HostsKpiQueries.kpiUniqueIps]: hostsKpiUniqueIps,
};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/index.ts
deleted file mode 100644
index f38e0f7b401aa..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/index.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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- HostsKpiQueries,
- HostsKpiHostsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/hosts';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildHostsKpiHostsQuery } from './query.hosts_kpi_hosts.dsl';
-import { formatGeneralHistogramData } from '../../../common/format_general_histogram_data';
-
-export const hostsKpiHosts: SecuritySolutionFactory = {
- buildDsl: (options) => buildHostsKpiHostsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildHostsKpiHostsQuery(options))],
- };
-
- const hostsHistogram = getOr(
- null,
- 'aggregations.hosts_histogram.buckets',
- response.rawResponse
- );
- return {
- ...response,
- inspect,
- hosts: getOr(null, 'aggregations.hosts.value', response.rawResponse),
- hostsHistogram: formatGeneralHistogramData(hostsHistogram),
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts
deleted file mode 100644
index 73e5c25e53683..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/hosts/query.hosts_kpi_hosts.dsl.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { KpiHostsRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-export const buildHostsKpiHostsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: KpiHostsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggregations: {
- hosts: {
- cardinality: {
- field: 'host.name',
- },
- },
- hosts_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- cardinality: {
- field: 'host.name',
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter,
- },
- },
- _source: false,
- fields: [
- 'host.name',
- {
- field: '@timestamp',
- format: 'strict_date_optional_time',
- },
- ],
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/index.ts
deleted file mode 100644
index 8d0eef56b75b9..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/index.ts
+++ /dev/null
@@ -1,55 +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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- HostsKpiQueries,
- HostsKpiUniqueIpsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/hosts';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildHostsKpiUniqueIpsQuery } from './query.hosts_kpi_unique_ips.dsl';
-import { formatGeneralHistogramData } from '../../../common/format_general_histogram_data';
-
-export const hostsKpiUniqueIps: SecuritySolutionFactory = {
- buildDsl: (options) => buildHostsKpiUniqueIpsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildHostsKpiUniqueIpsQuery(options))],
- };
-
- const uniqueSourceIpsHistogram = getOr(
- null,
- 'aggregations.unique_source_ips_histogram.buckets',
- response.rawResponse
- );
-
- const uniqueDestinationIpsHistogram = getOr(
- null,
- 'aggregations.unique_destination_ips_histogram.buckets',
- response.rawResponse
- );
-
- return {
- ...response,
- inspect,
- uniqueSourceIps: getOr(null, 'aggregations.unique_source_ips.value', response.rawResponse),
- uniqueSourceIpsHistogram: formatGeneralHistogramData(uniqueSourceIpsHistogram),
- uniqueDestinationIps: getOr(
- null,
- 'aggregations.unique_destination_ips.value',
- response.rawResponse
- ),
- uniqueDestinationIpsHistogram: formatGeneralHistogramData(uniqueDestinationIpsHistogram),
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts
deleted file mode 100644
index 0d18765a1b6a4..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/kpi/unique_ips/query.hosts_kpi_unique_ips.dsl.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { KpiUniqueIpsRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-export const buildHostsKpiUniqueIpsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: KpiUniqueIpsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggregations: {
- unique_source_ips: {
- cardinality: {
- field: 'source.ip',
- },
- },
- unique_source_ips_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- cardinality: {
- field: 'source.ip',
- },
- },
- },
- },
- unique_destination_ips: {
- cardinality: {
- field: 'destination.ip',
- },
- },
- unique_destination_ips_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- cardinality: {
- field: 'destination.ip',
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter,
- },
- },
- _source: false,
- fields: [
- 'destination.ip',
- 'source.ip',
- {
- field: '@timestamp',
- format: 'strict_date_optional_time',
- },
- ],
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts
index af94f47be89de..46ef72464dd1b 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/index.ts
@@ -9,7 +9,6 @@ import type { FactoryQueryTypes } from '../../../../common/search_strategy/secur
import type { SecuritySolutionFactory } from './types';
import { hostsFactory } from './hosts';
-import { matrixHistogramFactory } from './matrix_histogram';
import { networkFactory } from './network';
import { ctiFactoryTypes } from './cti';
import { riskScoreFactory } from './risk_score';
@@ -23,7 +22,6 @@ export const securitySolutionFactory: Record<
> = {
...hostsFactory,
...usersFactory,
- ...matrixHistogramFactory,
...networkFactory,
...ctiFactoryTypes,
...riskScoreFactory,
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts
deleted file mode 100644
index 5f0b69ba6dc87..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/__mocks__/index.ts
+++ /dev/null
@@ -1,2180 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-
-import type { MatrixHistogramStrategyResponse } from '../../../../../../common/search_strategy';
-
-export const mockAlertsSearchStrategyResponse: IEsSearchResponse = {
- isPartial: false,
- isRunning: false,
- rawResponse: {
- took: 11,
- timed_out: false,
- _shards: { total: 21, successful: 21, skipped: 0, failed: 0 },
- hits: { total: 0, max_score: 0, hits: [] },
- aggregations: {
- alertsGroup: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] },
- },
- },
- total: 21,
- loaded: 21,
-};
-
-export const formattedAlertsSearchStrategyResponse: MatrixHistogramStrategyResponse = {
- ...mockAlertsSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- alertsGroup: {
- terms: {
- field: 'event.module',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- alerts: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599574984482, max: 1599661384482 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- {
- bool: {
- filter: [
- {
- bool: {
- should: [{ exists: { field: 'host.name' } }],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- ],
- should: [],
- must_not: [],
- },
- },
- {
- bool: {
- filter: [
- {
- bool: {
- should: [{ match: { 'event.kind': 'alert' } }],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T14:23:04.482Z',
- lte: '2020-09-09T14:23:04.482Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
- },
- null,
- 2
- ),
- ],
- },
- matrixHistogramData: [],
- totalCount: 0,
-};
-
-export const expectedDsl = {
- allow_no_indices: true,
- track_total_hits: false,
- body: {
- aggregations: {
- host_count: { cardinality: { field: 'host.name' } },
- host_data: {
- aggs: {
- lastSeen: { max: { field: '@timestamp' } },
- os: {
- top_hits: {
- _source: { includes: ['host.os.*'] },
- size: 1,
- sort: [{ '@timestamp': { order: 'desc' } }],
- },
- },
- },
- terms: { field: 'host.name', order: { lastSeen: 'desc' }, size: 10 },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } },
- {
- range: {
- '@timestamp': {
- format: 'strict_date_optional_time',
- gte: '2020-09-03T09:15:21.415Z',
- lte: '2020-09-04T09:15:21.415Z',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
- ignore_unavailable: true,
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
-};
-
-export const mockAnomaliesSearchStrategyResponse: IEsSearchResponse = {
- isPartial: false,
- isRunning: false,
- rawResponse: {
- took: 9,
- timed_out: false,
- _shards: { total: 21, successful: 21, skipped: 0, failed: 0 },
- hits: { total: 0, max_score: 0, hits: [] },
- aggregations: {
- anomalyActionGroup: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] },
- },
- },
- total: 21,
- loaded: 21,
-};
-
-export const formattedAnomaliesSearchStrategyResponse: MatrixHistogramStrategyResponse = {
- ...mockAnomaliesSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggs: {
- anomalyActionGroup: {
- terms: { field: 'job_id', order: { _count: 'desc' }, size: 10 },
- aggs: {
- anomalies: {
- date_histogram: {
- field: 'timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599578075566, max: 1599664475566 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- { bool: { should: [], minimum_should_match: 1 } },
- { match_phrase: { result_type: 'record' } },
- null,
- { range: { record_score: { gte: 50 } } },
- ],
- should: [
- { exists: { field: 'source.ip' } },
- { exists: { field: 'destination.ip' } },
- ],
- must_not: [],
- minimum_should_match: 1,
- },
- },
- {
- range: {
- timestamp: {
- gte: '2020-09-08T15:14:35.566Z',
- lte: '2020-09-09T15:14:35.566Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
- },
- null,
- 2
- ),
- ],
- },
- matrixHistogramData: [],
- totalCount: 0,
-};
-
-export const mockAuthenticationsSearchStrategyResponse: IEsSearchResponse = {
- isPartial: false,
- isRunning: false,
- rawResponse: {
- took: 6,
- timed_out: false,
- _shards: { total: 21, successful: 21, skipped: 0, failed: 0 },
- hits: { total: 0, max_score: 0, hits: [] },
- aggregations: {
- eventActionGroup: {
- doc_count_error_upper_bound: 0,
- sum_other_doc_count: 0,
- buckets: [
- {
- key: 'failure',
- doc_count: 379,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:00:00.000Z', key: 1599577200000, doc_count: 1 },
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 4 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 34 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 1 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 3 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 20 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 24 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 37 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 4 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 22 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 3 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 0 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 2 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 21 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 28 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 30 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 19 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 4 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 1 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 6 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 18 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 5 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 23 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 15 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 2 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 0 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 5 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 2 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 4 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 6 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 11 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 0 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 24 },
- ],
- },
- },
- {
- key: 'success',
- doc_count: 191,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:00:00.000Z', key: 1599577200000, doc_count: 2 },
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 5 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 5 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 2 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 4 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 6 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 4 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 13 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 6 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 3 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 1 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 9 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 5 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 6 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 8 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 2 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 9 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 2 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 5 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 2 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 14 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 7 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 13 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 10 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 5 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 2 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 6 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 7 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 5 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 6 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 5 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 10 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 2 },
- ],
- },
- },
- ],
- },
- },
- },
- total: 21,
- loaded: 21,
-};
-
-export const formattedAuthenticationsSearchStrategyResponse: MatrixHistogramStrategyResponse = {
- ...mockAuthenticationsSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'event.outcome',
- include: ['success', 'failure'],
- order: { _count: 'desc' },
- size: 2,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599578520325, max: 1599664920325 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- { bool: { must: [{ term: { 'event.category': 'authentication' } }] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T15:22:00.325Z',
- lte: '2020-09-09T15:22:00.325Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
- },
- null,
- 2
- ),
- ],
- },
- matrixHistogramData: [
- { x: 1599577200000, y: 1, g: 'failure' },
- { x: 1599579900000, y: 4, g: 'failure' },
- { x: 1599582600000, y: 34, g: 'failure' },
- { x: 1599585300000, y: 1, g: 'failure' },
- { x: 1599588000000, y: 3, g: 'failure' },
- { x: 1599590700000, y: 20, g: 'failure' },
- { x: 1599593400000, y: 24, g: 'failure' },
- { x: 1599596100000, y: 37, g: 'failure' },
- { x: 1599598800000, y: 4, g: 'failure' },
- { x: 1599601500000, y: 22, g: 'failure' },
- { x: 1599604200000, y: 3, g: 'failure' },
- { x: 1599606900000, y: 0, g: 'failure' },
- { x: 1599609600000, y: 2, g: 'failure' },
- { x: 1599612300000, y: 21, g: 'failure' },
- { x: 1599615000000, y: 28, g: 'failure' },
- { x: 1599617700000, y: 30, g: 'failure' },
- { x: 1599620400000, y: 19, g: 'failure' },
- { x: 1599623100000, y: 4, g: 'failure' },
- { x: 1599625800000, y: 1, g: 'failure' },
- { x: 1599628500000, y: 6, g: 'failure' },
- { x: 1599631200000, y: 18, g: 'failure' },
- { x: 1599633900000, y: 5, g: 'failure' },
- { x: 1599636600000, y: 23, g: 'failure' },
- { x: 1599639300000, y: 15, g: 'failure' },
- { x: 1599642000000, y: 2, g: 'failure' },
- { x: 1599644700000, y: 0, g: 'failure' },
- { x: 1599647400000, y: 5, g: 'failure' },
- { x: 1599650100000, y: 2, g: 'failure' },
- { x: 1599652800000, y: 4, g: 'failure' },
- { x: 1599655500000, y: 6, g: 'failure' },
- { x: 1599658200000, y: 11, g: 'failure' },
- { x: 1599660900000, y: 0, g: 'failure' },
- { x: 1599663600000, y: 24, g: 'failure' },
- { x: 1599577200000, y: 2, g: 'success' },
- { x: 1599579900000, y: 5, g: 'success' },
- { x: 1599582600000, y: 5, g: 'success' },
- { x: 1599585300000, y: 2, g: 'success' },
- { x: 1599588000000, y: 4, g: 'success' },
- { x: 1599590700000, y: 6, g: 'success' },
- { x: 1599593400000, y: 4, g: 'success' },
- { x: 1599596100000, y: 13, g: 'success' },
- { x: 1599598800000, y: 6, g: 'success' },
- { x: 1599601500000, y: 3, g: 'success' },
- { x: 1599604200000, y: 1, g: 'success' },
- { x: 1599606900000, y: 9, g: 'success' },
- { x: 1599609600000, y: 5, g: 'success' },
- { x: 1599612300000, y: 6, g: 'success' },
- { x: 1599615000000, y: 8, g: 'success' },
- { x: 1599617700000, y: 2, g: 'success' },
- { x: 1599620400000, y: 9, g: 'success' },
- { x: 1599623100000, y: 2, g: 'success' },
- { x: 1599625800000, y: 5, g: 'success' },
- { x: 1599628500000, y: 2, g: 'success' },
- { x: 1599631200000, y: 14, g: 'success' },
- { x: 1599633900000, y: 7, g: 'success' },
- { x: 1599636600000, y: 13, g: 'success' },
- { x: 1599639300000, y: 10, g: 'success' },
- { x: 1599642000000, y: 5, g: 'success' },
- { x: 1599644700000, y: 2, g: 'success' },
- { x: 1599647400000, y: 6, g: 'success' },
- { x: 1599650100000, y: 7, g: 'success' },
- { x: 1599652800000, y: 5, g: 'success' },
- { x: 1599655500000, y: 6, g: 'success' },
- { x: 1599658200000, y: 5, g: 'success' },
- { x: 1599660900000, y: 10, g: 'success' },
- { x: 1599663600000, y: 2, g: 'success' },
- ],
- totalCount: 0,
-};
-
-export const mockEventsSearchStrategyResponse: IEsSearchResponse = {
- isPartial: false,
- isRunning: false,
- rawResponse: {
- took: 198,
- timed_out: false,
- _shards: { total: 21, successful: 21, skipped: 0, failed: 0 },
- hits: { total: 0, max_score: 0, hits: [] },
- aggregations: {
- eventActionGroup: {
- doc_count_error_upper_bound: 3,
- sum_other_doc_count: 4090,
- buckets: [
- {
- key: 'All others',
- doc_count: 1556741,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 26124 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 62910 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 60326 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 56144 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 53614 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53228 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 61195 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 52082 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 52697 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 41094 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 50164 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 41500 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 42373 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 49785 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 42237 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 43114 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 40716 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 39248 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 37674 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 41072 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 37049 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 38561 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 40895 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 45490 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 46559 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 40020 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 44335 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 47252 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 48744 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 55756 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 56887 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 66920 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 40976 },
- ],
- },
- },
- {
- key: 'end',
- doc_count: 18413,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 226 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 547 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 532 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 551 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 543 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 547 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 656 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 543 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 616 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 539 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 539 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 547 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 616 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 640 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 614 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 545 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 537 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 544 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 571 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 743 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 560 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 598 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 613 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 563 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 540 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 538 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 549 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 561 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 554 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 561 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 542 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 712 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 326 },
- ],
- },
- },
- {
- key: 'fork',
- doc_count: 18412,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 226 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 546 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 532 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 551 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 543 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 547 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 656 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 543 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 616 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 539 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 539 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 547 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 616 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 640 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 614 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 545 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 537 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 544 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 571 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 743 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 560 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 598 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 613 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 563 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 540 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 538 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 549 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 561 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 554 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 561 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 542 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 712 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 326 },
- ],
- },
- },
- {
- key: 'exec',
- doc_count: 15183,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 189 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 456 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 445 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 458 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 455 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 457 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 511 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 455 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 493 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 451 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 453 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 460 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 521 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 504 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 490 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 457 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 447 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 454 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 469 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 642 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 465 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 481 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 489 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 466 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 452 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 448 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 457 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 471 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 460 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 463 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 455 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 547 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 262 },
- ],
- },
- },
- {
- key: 'disconnect_received',
- doc_count: 4998,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 59 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 151 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 139 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 144 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 143 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 144 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 202 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 142 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 180 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 144 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 143 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 137 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 150 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 195 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 178 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 144 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 143 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 142 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 157 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 166 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 153 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 168 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 175 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 158 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 142 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 144 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 147 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 139 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 145 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 158 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 137 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 234 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 95 },
- ],
- },
- },
- {
- key: 'connection_attempted',
- doc_count: 4534,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 60 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 145 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 138 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 144 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 140 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 144 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 145 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 137 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 142 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 142 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 143 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 132 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 153 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 143 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 142 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 143 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 142 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 140 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 140 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 148 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 142 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 139 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 139 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 142 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 142 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 143 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 141 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 137 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 141 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 144 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 138 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 145 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 78 },
- ],
- },
- },
- {
- key: 'creation',
- doc_count: 1880,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 24 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 53 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 50 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 54 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 55 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 54 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 54 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 55 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 52 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 51 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 58 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 122 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 54 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 54 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 56 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 53 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 55 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 51 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 144 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 54 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 53 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 51 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 57 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 55 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 52 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 52 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 57 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 56 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 53 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 51 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 56 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 31 },
- ],
- },
- },
- {
- key: 'deletion',
- doc_count: 1869,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 23 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 53 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 50 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 54 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 54 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 53 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 53 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 54 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 55 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 52 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 51 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 55 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 121 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 54 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 53 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 55 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 53 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 54 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 51 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 146 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 54 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 53 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 51 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 55 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 55 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 52 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 55 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 55 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 55 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 53 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 51 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 55 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 31 },
- ],
- },
- },
- {
- key: 'File Delete (rule: FileDelete)',
- doc_count: 1831,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 19 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 46 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 47 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 47 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 47 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 45 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 48 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 46 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 45 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 47 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 47 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 60 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 45 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 46 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 46 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 47 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 88 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 53 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 46 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 49 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 45 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 48 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 46 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 46 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 45 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 47 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 47 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 45 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 331 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 45 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 47 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 47 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 28 },
- ],
- },
- },
- {
- key: 'session_id_change',
- doc_count: 647,
- events: {
- buckets: [
- { key_as_string: '2020-09-08T15:45:00.000Z', key: 1599579900000, doc_count: 3 },
- { key_as_string: '2020-09-08T16:30:00.000Z', key: 1599582600000, doc_count: 9 },
- { key_as_string: '2020-09-08T17:15:00.000Z', key: 1599585300000, doc_count: 7 },
- { key_as_string: '2020-09-08T18:00:00.000Z', key: 1599588000000, doc_count: 10 },
- { key_as_string: '2020-09-08T18:45:00.000Z', key: 1599590700000, doc_count: 7 },
- { key_as_string: '2020-09-08T19:30:00.000Z', key: 1599593400000, doc_count: 10 },
- { key_as_string: '2020-09-08T20:15:00.000Z', key: 1599596100000, doc_count: 63 },
- { key_as_string: '2020-09-08T21:00:00.000Z', key: 1599598800000, doc_count: 7 },
- { key_as_string: '2020-09-08T21:45:00.000Z', key: 1599601500000, doc_count: 45 },
- { key_as_string: '2020-09-08T22:30:00.000Z', key: 1599604200000, doc_count: 4 },
- { key_as_string: '2020-09-08T23:15:00.000Z', key: 1599606900000, doc_count: 5 },
- { key_as_string: '2020-09-09T00:00:00.000Z', key: 1599609600000, doc_count: 6 },
- { key_as_string: '2020-09-09T00:45:00.000Z', key: 1599612300000, doc_count: 6 },
- { key_as_string: '2020-09-09T01:30:00.000Z', key: 1599615000000, doc_count: 55 },
- { key_as_string: '2020-09-09T02:15:00.000Z', key: 1599617700000, doc_count: 43 },
- { key_as_string: '2020-09-09T03:00:00.000Z', key: 1599620400000, doc_count: 8 },
- { key_as_string: '2020-09-09T03:45:00.000Z', key: 1599623100000, doc_count: 9 },
- { key_as_string: '2020-09-09T04:30:00.000Z', key: 1599625800000, doc_count: 7 },
- { key_as_string: '2020-09-09T05:15:00.000Z', key: 1599628500000, doc_count: 21 },
- { key_as_string: '2020-09-09T06:00:00.000Z', key: 1599631200000, doc_count: 26 },
- { key_as_string: '2020-09-09T06:45:00.000Z', key: 1599633900000, doc_count: 17 },
- { key_as_string: '2020-09-09T07:30:00.000Z', key: 1599636600000, doc_count: 34 },
- { key_as_string: '2020-09-09T08:15:00.000Z', key: 1599639300000, doc_count: 41 },
- { key_as_string: '2020-09-09T09:00:00.000Z', key: 1599642000000, doc_count: 18 },
- { key_as_string: '2020-09-09T09:45:00.000Z', key: 1599644700000, doc_count: 4 },
- { key_as_string: '2020-09-09T10:30:00.000Z', key: 1599647400000, doc_count: 11 },
- { key_as_string: '2020-09-09T11:15:00.000Z', key: 1599650100000, doc_count: 9 },
- { key_as_string: '2020-09-09T12:00:00.000Z', key: 1599652800000, doc_count: 7 },
- { key_as_string: '2020-09-09T12:45:00.000Z', key: 1599655500000, doc_count: 12 },
- { key_as_string: '2020-09-09T13:30:00.000Z', key: 1599658200000, doc_count: 16 },
- { key_as_string: '2020-09-09T14:15:00.000Z', key: 1599660900000, doc_count: 7 },
- { key_as_string: '2020-09-09T15:00:00.000Z', key: 1599663600000, doc_count: 99 },
- { key_as_string: '2020-09-09T15:45:00.000Z', key: 1599666300000, doc_count: 21 },
- ],
- },
- },
- ],
- },
- },
- },
- total: 21,
- loaded: 21,
-};
-
-export const formattedEventsSearchStrategyResponse: MatrixHistogramStrategyResponse = {
- ...mockEventsSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'event.action',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: {
- '@a.runtime.field': {
- script: {
- source: 'emit("Radically mocked dude: " + doc[\'host.name\'].value)',
- },
- type: 'keyword',
- },
- },
- size: 0,
- },
- },
- null,
- 2
- ),
- ],
- },
- totalCount: 0,
- matrixHistogramData: [
- { x: 1599579900000, y: 26124, g: 'All others' },
- { x: 1599582600000, y: 62910, g: 'All others' },
- { x: 1599585300000, y: 60326, g: 'All others' },
- { x: 1599588000000, y: 56144, g: 'All others' },
- { x: 1599590700000, y: 53614, g: 'All others' },
- { x: 1599593400000, y: 53228, g: 'All others' },
- { x: 1599596100000, y: 61195, g: 'All others' },
- { x: 1599598800000, y: 52082, g: 'All others' },
- { x: 1599601500000, y: 52697, g: 'All others' },
- { x: 1599604200000, y: 41094, g: 'All others' },
- { x: 1599606900000, y: 50164, g: 'All others' },
- { x: 1599609600000, y: 41500, g: 'All others' },
- { x: 1599612300000, y: 42373, g: 'All others' },
- { x: 1599615000000, y: 49785, g: 'All others' },
- { x: 1599617700000, y: 42237, g: 'All others' },
- { x: 1599620400000, y: 43114, g: 'All others' },
- { x: 1599623100000, y: 40716, g: 'All others' },
- { x: 1599625800000, y: 39248, g: 'All others' },
- { x: 1599628500000, y: 37674, g: 'All others' },
- { x: 1599631200000, y: 41072, g: 'All others' },
- { x: 1599633900000, y: 37049, g: 'All others' },
- { x: 1599636600000, y: 38561, g: 'All others' },
- { x: 1599639300000, y: 40895, g: 'All others' },
- { x: 1599642000000, y: 45490, g: 'All others' },
- { x: 1599644700000, y: 46559, g: 'All others' },
- { x: 1599647400000, y: 40020, g: 'All others' },
- { x: 1599650100000, y: 44335, g: 'All others' },
- { x: 1599652800000, y: 47252, g: 'All others' },
- { x: 1599655500000, y: 48744, g: 'All others' },
- { x: 1599658200000, y: 55756, g: 'All others' },
- { x: 1599660900000, y: 56887, g: 'All others' },
- { x: 1599663600000, y: 66920, g: 'All others' },
- { x: 1599666300000, y: 40976, g: 'All others' },
- { x: 1599579900000, y: 226, g: 'end' },
- { x: 1599582600000, y: 547, g: 'end' },
- { x: 1599585300000, y: 532, g: 'end' },
- { x: 1599588000000, y: 551, g: 'end' },
- { x: 1599590700000, y: 543, g: 'end' },
- { x: 1599593400000, y: 547, g: 'end' },
- { x: 1599596100000, y: 656, g: 'end' },
- { x: 1599598800000, y: 543, g: 'end' },
- { x: 1599601500000, y: 616, g: 'end' },
- { x: 1599604200000, y: 539, g: 'end' },
- { x: 1599606900000, y: 539, g: 'end' },
- { x: 1599609600000, y: 547, g: 'end' },
- { x: 1599612300000, y: 616, g: 'end' },
- { x: 1599615000000, y: 640, g: 'end' },
- { x: 1599617700000, y: 614, g: 'end' },
- { x: 1599620400000, y: 545, g: 'end' },
- { x: 1599623100000, y: 537, g: 'end' },
- { x: 1599625800000, y: 544, g: 'end' },
- { x: 1599628500000, y: 571, g: 'end' },
- { x: 1599631200000, y: 743, g: 'end' },
- { x: 1599633900000, y: 560, g: 'end' },
- { x: 1599636600000, y: 598, g: 'end' },
- { x: 1599639300000, y: 613, g: 'end' },
- { x: 1599642000000, y: 563, g: 'end' },
- { x: 1599644700000, y: 540, g: 'end' },
- { x: 1599647400000, y: 538, g: 'end' },
- { x: 1599650100000, y: 549, g: 'end' },
- { x: 1599652800000, y: 561, g: 'end' },
- { x: 1599655500000, y: 554, g: 'end' },
- { x: 1599658200000, y: 561, g: 'end' },
- { x: 1599660900000, y: 542, g: 'end' },
- { x: 1599663600000, y: 712, g: 'end' },
- { x: 1599666300000, y: 326, g: 'end' },
- { x: 1599579900000, y: 226, g: 'fork' },
- { x: 1599582600000, y: 546, g: 'fork' },
- { x: 1599585300000, y: 532, g: 'fork' },
- { x: 1599588000000, y: 551, g: 'fork' },
- { x: 1599590700000, y: 543, g: 'fork' },
- { x: 1599593400000, y: 547, g: 'fork' },
- { x: 1599596100000, y: 656, g: 'fork' },
- { x: 1599598800000, y: 543, g: 'fork' },
- { x: 1599601500000, y: 616, g: 'fork' },
- { x: 1599604200000, y: 539, g: 'fork' },
- { x: 1599606900000, y: 539, g: 'fork' },
- { x: 1599609600000, y: 547, g: 'fork' },
- { x: 1599612300000, y: 616, g: 'fork' },
- { x: 1599615000000, y: 640, g: 'fork' },
- { x: 1599617700000, y: 614, g: 'fork' },
- { x: 1599620400000, y: 545, g: 'fork' },
- { x: 1599623100000, y: 537, g: 'fork' },
- { x: 1599625800000, y: 544, g: 'fork' },
- { x: 1599628500000, y: 571, g: 'fork' },
- { x: 1599631200000, y: 743, g: 'fork' },
- { x: 1599633900000, y: 560, g: 'fork' },
- { x: 1599636600000, y: 598, g: 'fork' },
- { x: 1599639300000, y: 613, g: 'fork' },
- { x: 1599642000000, y: 563, g: 'fork' },
- { x: 1599644700000, y: 540, g: 'fork' },
- { x: 1599647400000, y: 538, g: 'fork' },
- { x: 1599650100000, y: 549, g: 'fork' },
- { x: 1599652800000, y: 561, g: 'fork' },
- { x: 1599655500000, y: 554, g: 'fork' },
- { x: 1599658200000, y: 561, g: 'fork' },
- { x: 1599660900000, y: 542, g: 'fork' },
- { x: 1599663600000, y: 712, g: 'fork' },
- { x: 1599666300000, y: 326, g: 'fork' },
- { x: 1599579900000, y: 189, g: 'exec' },
- { x: 1599582600000, y: 456, g: 'exec' },
- { x: 1599585300000, y: 445, g: 'exec' },
- { x: 1599588000000, y: 458, g: 'exec' },
- { x: 1599590700000, y: 455, g: 'exec' },
- { x: 1599593400000, y: 457, g: 'exec' },
- { x: 1599596100000, y: 511, g: 'exec' },
- { x: 1599598800000, y: 455, g: 'exec' },
- { x: 1599601500000, y: 493, g: 'exec' },
- { x: 1599604200000, y: 451, g: 'exec' },
- { x: 1599606900000, y: 453, g: 'exec' },
- { x: 1599609600000, y: 460, g: 'exec' },
- { x: 1599612300000, y: 521, g: 'exec' },
- { x: 1599615000000, y: 504, g: 'exec' },
- { x: 1599617700000, y: 490, g: 'exec' },
- { x: 1599620400000, y: 457, g: 'exec' },
- { x: 1599623100000, y: 447, g: 'exec' },
- { x: 1599625800000, y: 454, g: 'exec' },
- { x: 1599628500000, y: 469, g: 'exec' },
- { x: 1599631200000, y: 642, g: 'exec' },
- { x: 1599633900000, y: 465, g: 'exec' },
- { x: 1599636600000, y: 481, g: 'exec' },
- { x: 1599639300000, y: 489, g: 'exec' },
- { x: 1599642000000, y: 466, g: 'exec' },
- { x: 1599644700000, y: 452, g: 'exec' },
- { x: 1599647400000, y: 448, g: 'exec' },
- { x: 1599650100000, y: 457, g: 'exec' },
- { x: 1599652800000, y: 471, g: 'exec' },
- { x: 1599655500000, y: 460, g: 'exec' },
- { x: 1599658200000, y: 463, g: 'exec' },
- { x: 1599660900000, y: 455, g: 'exec' },
- { x: 1599663600000, y: 547, g: 'exec' },
- { x: 1599666300000, y: 262, g: 'exec' },
- { x: 1599579900000, y: 59, g: 'disconnect_received' },
- { x: 1599582600000, y: 151, g: 'disconnect_received' },
- { x: 1599585300000, y: 139, g: 'disconnect_received' },
- { x: 1599588000000, y: 144, g: 'disconnect_received' },
- { x: 1599590700000, y: 143, g: 'disconnect_received' },
- { x: 1599593400000, y: 144, g: 'disconnect_received' },
- { x: 1599596100000, y: 202, g: 'disconnect_received' },
- { x: 1599598800000, y: 142, g: 'disconnect_received' },
- { x: 1599601500000, y: 180, g: 'disconnect_received' },
- { x: 1599604200000, y: 144, g: 'disconnect_received' },
- { x: 1599606900000, y: 143, g: 'disconnect_received' },
- { x: 1599609600000, y: 137, g: 'disconnect_received' },
- { x: 1599612300000, y: 150, g: 'disconnect_received' },
- { x: 1599615000000, y: 195, g: 'disconnect_received' },
- { x: 1599617700000, y: 178, g: 'disconnect_received' },
- { x: 1599620400000, y: 144, g: 'disconnect_received' },
- { x: 1599623100000, y: 143, g: 'disconnect_received' },
- { x: 1599625800000, y: 142, g: 'disconnect_received' },
- { x: 1599628500000, y: 157, g: 'disconnect_received' },
- { x: 1599631200000, y: 166, g: 'disconnect_received' },
- { x: 1599633900000, y: 153, g: 'disconnect_received' },
- { x: 1599636600000, y: 168, g: 'disconnect_received' },
- { x: 1599639300000, y: 175, g: 'disconnect_received' },
- { x: 1599642000000, y: 158, g: 'disconnect_received' },
- { x: 1599644700000, y: 142, g: 'disconnect_received' },
- { x: 1599647400000, y: 144, g: 'disconnect_received' },
- { x: 1599650100000, y: 147, g: 'disconnect_received' },
- { x: 1599652800000, y: 139, g: 'disconnect_received' },
- { x: 1599655500000, y: 145, g: 'disconnect_received' },
- { x: 1599658200000, y: 158, g: 'disconnect_received' },
- { x: 1599660900000, y: 137, g: 'disconnect_received' },
- { x: 1599663600000, y: 234, g: 'disconnect_received' },
- { x: 1599666300000, y: 95, g: 'disconnect_received' },
- { x: 1599579900000, y: 60, g: 'connection_attempted' },
- { x: 1599582600000, y: 145, g: 'connection_attempted' },
- { x: 1599585300000, y: 138, g: 'connection_attempted' },
- { x: 1599588000000, y: 144, g: 'connection_attempted' },
- { x: 1599590700000, y: 140, g: 'connection_attempted' },
- { x: 1599593400000, y: 144, g: 'connection_attempted' },
- { x: 1599596100000, y: 145, g: 'connection_attempted' },
- { x: 1599598800000, y: 137, g: 'connection_attempted' },
- { x: 1599601500000, y: 142, g: 'connection_attempted' },
- { x: 1599604200000, y: 142, g: 'connection_attempted' },
- { x: 1599606900000, y: 143, g: 'connection_attempted' },
- { x: 1599609600000, y: 132, g: 'connection_attempted' },
- { x: 1599612300000, y: 153, g: 'connection_attempted' },
- { x: 1599615000000, y: 143, g: 'connection_attempted' },
- { x: 1599617700000, y: 142, g: 'connection_attempted' },
- { x: 1599620400000, y: 143, g: 'connection_attempted' },
- { x: 1599623100000, y: 142, g: 'connection_attempted' },
- { x: 1599625800000, y: 140, g: 'connection_attempted' },
- { x: 1599628500000, y: 140, g: 'connection_attempted' },
- { x: 1599631200000, y: 148, g: 'connection_attempted' },
- { x: 1599633900000, y: 142, g: 'connection_attempted' },
- { x: 1599636600000, y: 139, g: 'connection_attempted' },
- { x: 1599639300000, y: 139, g: 'connection_attempted' },
- { x: 1599642000000, y: 142, g: 'connection_attempted' },
- { x: 1599644700000, y: 142, g: 'connection_attempted' },
- { x: 1599647400000, y: 143, g: 'connection_attempted' },
- { x: 1599650100000, y: 141, g: 'connection_attempted' },
- { x: 1599652800000, y: 137, g: 'connection_attempted' },
- { x: 1599655500000, y: 141, g: 'connection_attempted' },
- { x: 1599658200000, y: 144, g: 'connection_attempted' },
- { x: 1599660900000, y: 138, g: 'connection_attempted' },
- { x: 1599663600000, y: 145, g: 'connection_attempted' },
- { x: 1599666300000, y: 78, g: 'connection_attempted' },
- { x: 1599579900000, y: 24, g: 'creation' },
- { x: 1599582600000, y: 53, g: 'creation' },
- { x: 1599585300000, y: 50, g: 'creation' },
- { x: 1599588000000, y: 54, g: 'creation' },
- { x: 1599590700000, y: 55, g: 'creation' },
- { x: 1599593400000, y: 53, g: 'creation' },
- { x: 1599596100000, y: 54, g: 'creation' },
- { x: 1599598800000, y: 54, g: 'creation' },
- { x: 1599601500000, y: 55, g: 'creation' },
- { x: 1599604200000, y: 52, g: 'creation' },
- { x: 1599606900000, y: 51, g: 'creation' },
- { x: 1599609600000, y: 58, g: 'creation' },
- { x: 1599612300000, y: 122, g: 'creation' },
- { x: 1599615000000, y: 54, g: 'creation' },
- { x: 1599617700000, y: 54, g: 'creation' },
- { x: 1599620400000, y: 56, g: 'creation' },
- { x: 1599623100000, y: 53, g: 'creation' },
- { x: 1599625800000, y: 55, g: 'creation' },
- { x: 1599628500000, y: 51, g: 'creation' },
- { x: 1599631200000, y: 144, g: 'creation' },
- { x: 1599633900000, y: 54, g: 'creation' },
- { x: 1599636600000, y: 53, g: 'creation' },
- { x: 1599639300000, y: 51, g: 'creation' },
- { x: 1599642000000, y: 57, g: 'creation' },
- { x: 1599644700000, y: 55, g: 'creation' },
- { x: 1599647400000, y: 52, g: 'creation' },
- { x: 1599650100000, y: 52, g: 'creation' },
- { x: 1599652800000, y: 57, g: 'creation' },
- { x: 1599655500000, y: 56, g: 'creation' },
- { x: 1599658200000, y: 53, g: 'creation' },
- { x: 1599660900000, y: 51, g: 'creation' },
- { x: 1599663600000, y: 56, g: 'creation' },
- { x: 1599666300000, y: 31, g: 'creation' },
- { x: 1599579900000, y: 23, g: 'deletion' },
- { x: 1599582600000, y: 53, g: 'deletion' },
- { x: 1599585300000, y: 50, g: 'deletion' },
- { x: 1599588000000, y: 54, g: 'deletion' },
- { x: 1599590700000, y: 54, g: 'deletion' },
- { x: 1599593400000, y: 53, g: 'deletion' },
- { x: 1599596100000, y: 53, g: 'deletion' },
- { x: 1599598800000, y: 54, g: 'deletion' },
- { x: 1599601500000, y: 55, g: 'deletion' },
- { x: 1599604200000, y: 52, g: 'deletion' },
- { x: 1599606900000, y: 51, g: 'deletion' },
- { x: 1599609600000, y: 55, g: 'deletion' },
- { x: 1599612300000, y: 121, g: 'deletion' },
- { x: 1599615000000, y: 54, g: 'deletion' },
- { x: 1599617700000, y: 53, g: 'deletion' },
- { x: 1599620400000, y: 55, g: 'deletion' },
- { x: 1599623100000, y: 53, g: 'deletion' },
- { x: 1599625800000, y: 54, g: 'deletion' },
- { x: 1599628500000, y: 51, g: 'deletion' },
- { x: 1599631200000, y: 146, g: 'deletion' },
- { x: 1599633900000, y: 54, g: 'deletion' },
- { x: 1599636600000, y: 53, g: 'deletion' },
- { x: 1599639300000, y: 51, g: 'deletion' },
- { x: 1599642000000, y: 55, g: 'deletion' },
- { x: 1599644700000, y: 55, g: 'deletion' },
- { x: 1599647400000, y: 52, g: 'deletion' },
- { x: 1599650100000, y: 55, g: 'deletion' },
- { x: 1599652800000, y: 55, g: 'deletion' },
- { x: 1599655500000, y: 55, g: 'deletion' },
- { x: 1599658200000, y: 53, g: 'deletion' },
- { x: 1599660900000, y: 51, g: 'deletion' },
- { x: 1599663600000, y: 55, g: 'deletion' },
- { x: 1599666300000, y: 31, g: 'deletion' },
- { x: 1599579900000, y: 19, g: 'File Delete (rule: FileDelete)' },
- { x: 1599582600000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599585300000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599588000000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599590700000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599593400000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599596100000, y: 48, g: 'File Delete (rule: FileDelete)' },
- { x: 1599598800000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599601500000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599604200000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599606900000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599609600000, y: 60, g: 'File Delete (rule: FileDelete)' },
- { x: 1599612300000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599615000000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599617700000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599620400000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599623100000, y: 88, g: 'File Delete (rule: FileDelete)' },
- { x: 1599625800000, y: 53, g: 'File Delete (rule: FileDelete)' },
- { x: 1599628500000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599631200000, y: 49, g: 'File Delete (rule: FileDelete)' },
- { x: 1599633900000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599636600000, y: 48, g: 'File Delete (rule: FileDelete)' },
- { x: 1599639300000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599642000000, y: 46, g: 'File Delete (rule: FileDelete)' },
- { x: 1599644700000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599647400000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599650100000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599652800000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599655500000, y: 331, g: 'File Delete (rule: FileDelete)' },
- { x: 1599658200000, y: 45, g: 'File Delete (rule: FileDelete)' },
- { x: 1599660900000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599663600000, y: 47, g: 'File Delete (rule: FileDelete)' },
- { x: 1599666300000, y: 28, g: 'File Delete (rule: FileDelete)' },
- { x: 1599579900000, y: 3, g: 'session_id_change' },
- { x: 1599582600000, y: 9, g: 'session_id_change' },
- { x: 1599585300000, y: 7, g: 'session_id_change' },
- { x: 1599588000000, y: 10, g: 'session_id_change' },
- { x: 1599590700000, y: 7, g: 'session_id_change' },
- { x: 1599593400000, y: 10, g: 'session_id_change' },
- { x: 1599596100000, y: 63, g: 'session_id_change' },
- { x: 1599598800000, y: 7, g: 'session_id_change' },
- { x: 1599601500000, y: 45, g: 'session_id_change' },
- { x: 1599604200000, y: 4, g: 'session_id_change' },
- { x: 1599606900000, y: 5, g: 'session_id_change' },
- { x: 1599609600000, y: 6, g: 'session_id_change' },
- { x: 1599612300000, y: 6, g: 'session_id_change' },
- { x: 1599615000000, y: 55, g: 'session_id_change' },
- { x: 1599617700000, y: 43, g: 'session_id_change' },
- { x: 1599620400000, y: 8, g: 'session_id_change' },
- { x: 1599623100000, y: 9, g: 'session_id_change' },
- { x: 1599625800000, y: 7, g: 'session_id_change' },
- { x: 1599628500000, y: 21, g: 'session_id_change' },
- { x: 1599631200000, y: 26, g: 'session_id_change' },
- { x: 1599633900000, y: 17, g: 'session_id_change' },
- { x: 1599636600000, y: 34, g: 'session_id_change' },
- { x: 1599639300000, y: 41, g: 'session_id_change' },
- { x: 1599642000000, y: 18, g: 'session_id_change' },
- { x: 1599644700000, y: 4, g: 'session_id_change' },
- { x: 1599647400000, y: 11, g: 'session_id_change' },
- { x: 1599650100000, y: 9, g: 'session_id_change' },
- { x: 1599652800000, y: 7, g: 'session_id_change' },
- { x: 1599655500000, y: 12, g: 'session_id_change' },
- { x: 1599658200000, y: 16, g: 'session_id_change' },
- { x: 1599660900000, y: 7, g: 'session_id_change' },
- { x: 1599663600000, y: 99, g: 'session_id_change' },
- { x: 1599666300000, y: 21, g: 'session_id_change' },
- ],
-};
-
-export const mockDnsSearchStrategyResponse: IEsSearchResponse = {
- isPartial: false,
- isRunning: false,
- rawResponse: {
- took: 36,
- timed_out: false,
- _shards: {
- total: 55,
- successful: 55,
- skipped: 38,
- failed: 0,
- },
- hits: {
- max_score: 0,
- hits: [],
- total: 0,
- },
- aggregations: {
- dns_count: {
- value: 3,
- },
- dns_name_query_count: {
- doc_count_error_upper_bound: 0,
- sum_other_doc_count: 0,
- buckets: [
- {
- key: 'google.com',
- doc_count: 1,
- unique_domains: {
- value: 1,
- },
- dns_question_name: {
- buckets: [
- {
- key_as_string: '2020-11-12T01:13:31.395Z',
- key: 1605143611395,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:21:48.492Z',
- key: 1605144108492,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:30:05.589Z',
- key: 1605144605589,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:38:22.686Z',
- key: 1605145102686,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:46:39.783Z',
- key: 1605145599783,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:54:56.880Z',
- key: 1605146096880,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:03:13.977Z',
- key: 1605146593977,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:11:31.074Z',
- key: 1605147091074,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:19:48.171Z',
- key: 1605147588171,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:28:05.268Z',
- key: 1605148085268,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:36:22.365Z',
- key: 1605148582365,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:44:39.462Z',
- key: 1605149079462,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:52:56.559Z',
- key: 1605149576559,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:01:13.656Z',
- key: 1605150073656,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:09:30.753Z',
- key: 1605150570753,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:17:47.850Z',
- key: 1605151067850,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:26:04.947Z',
- key: 1605151564947,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:34:22.044Z',
- key: 1605152062044,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:42:39.141Z',
- key: 1605152559141,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:50:56.238Z',
- key: 1605153056238,
- doc_count: 1,
- },
- {
- key_as_string: '2020-11-12T03:59:13.335Z',
- key: 1605153553335,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:07:30.432Z',
- key: 1605154050432,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:15:47.529Z',
- key: 1605154547529,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:24:04.626Z',
- key: 1605155044626,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:32:21.723Z',
- key: 1605155541723,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:40:38.820Z',
- key: 1605156038820,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:48:55.917Z',
- key: 1605156535917,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:57:13.014Z',
- key: 1605157033014,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:05:30.111Z',
- key: 1605157530111,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:13:47.208Z',
- key: 1605158027208,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:22:04.305Z',
- key: 1605158524305,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:30:21.402Z',
- key: 1605159021402,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:38:38.499Z',
- key: 1605159518499,
- doc_count: 0,
- },
- ],
- },
- dns_bytes_in: {
- value: 0,
- },
- dns_bytes_out: {
- value: 0,
- },
- },
- {
- key: 'google.internal',
- doc_count: 1,
- unique_domains: {
- value: 1,
- },
- dns_question_name: {
- buckets: [
- {
- key_as_string: '2020-11-12T01:13:31.395Z',
- key: 1605143611395,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:21:48.492Z',
- key: 1605144108492,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:30:05.589Z',
- key: 1605144605589,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:38:22.686Z',
- key: 1605145102686,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:46:39.783Z',
- key: 1605145599783,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:54:56.880Z',
- key: 1605146096880,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:03:13.977Z',
- key: 1605146593977,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:11:31.074Z',
- key: 1605147091074,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:19:48.171Z',
- key: 1605147588171,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:28:05.268Z',
- key: 1605148085268,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:36:22.365Z',
- key: 1605148582365,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:44:39.462Z',
- key: 1605149079462,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:52:56.559Z',
- key: 1605149576559,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:01:13.656Z',
- key: 1605150073656,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:09:30.753Z',
- key: 1605150570753,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:17:47.850Z',
- key: 1605151067850,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:26:04.947Z',
- key: 1605151564947,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:34:22.044Z',
- key: 1605152062044,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:42:39.141Z',
- key: 1605152559141,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:50:56.238Z',
- key: 1605153056238,
- doc_count: 1,
- },
- {
- key_as_string: '2020-11-12T03:59:13.335Z',
- key: 1605153553335,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:07:30.432Z',
- key: 1605154050432,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:15:47.529Z',
- key: 1605154547529,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:24:04.626Z',
- key: 1605155044626,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:32:21.723Z',
- key: 1605155541723,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:40:38.820Z',
- key: 1605156038820,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:48:55.917Z',
- key: 1605156535917,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:57:13.014Z',
- key: 1605157033014,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:05:30.111Z',
- key: 1605157530111,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:13:47.208Z',
- key: 1605158027208,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:22:04.305Z',
- key: 1605158524305,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:30:21.402Z',
- key: 1605159021402,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:38:38.499Z',
- key: 1605159518499,
- doc_count: 0,
- },
- ],
- },
- dns_bytes_in: {
- value: 0,
- },
- dns_bytes_out: {
- value: 0,
- },
- },
- {
- key: 'windows.net',
- doc_count: 1,
- unique_domains: {
- value: 1,
- },
- dns_question_name: {
- buckets: [
- {
- key_as_string: '2020-11-12T01:13:31.395Z',
- key: 1605143611395,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:21:48.492Z',
- key: 1605144108492,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:30:05.589Z',
- key: 1605144605589,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:38:22.686Z',
- key: 1605145102686,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:46:39.783Z',
- key: 1605145599783,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T01:54:56.880Z',
- key: 1605146096880,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:03:13.977Z',
- key: 1605146593977,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:11:31.074Z',
- key: 1605147091074,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:19:48.171Z',
- key: 1605147588171,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:28:05.268Z',
- key: 1605148085268,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:36:22.365Z',
- key: 1605148582365,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:44:39.462Z',
- key: 1605149079462,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T02:52:56.559Z',
- key: 1605149576559,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:01:13.656Z',
- key: 1605150073656,
- doc_count: 1,
- },
- {
- key_as_string: '2020-11-12T03:09:30.753Z',
- key: 1605150570753,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:17:47.850Z',
- key: 1605151067850,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:26:04.947Z',
- key: 1605151564947,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:34:22.044Z',
- key: 1605152062044,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:42:39.141Z',
- key: 1605152559141,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:50:56.238Z',
- key: 1605153056238,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T03:59:13.335Z',
- key: 1605153553335,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:07:30.432Z',
- key: 1605154050432,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:15:47.529Z',
- key: 1605154547529,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:24:04.626Z',
- key: 1605155044626,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:32:21.723Z',
- key: 1605155541723,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:40:38.820Z',
- key: 1605156038820,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:48:55.917Z',
- key: 1605156535917,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T04:57:13.014Z',
- key: 1605157033014,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:05:30.111Z',
- key: 1605157530111,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:13:47.208Z',
- key: 1605158027208,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:22:04.305Z',
- key: 1605158524305,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:30:21.402Z',
- key: 1605159021402,
- doc_count: 0,
- },
- {
- key_as_string: '2020-11-12T05:38:38.499Z',
- key: 1605159518499,
- doc_count: 0,
- },
- ],
- },
- dns_bytes_in: {
- value: 0,
- },
- dns_bytes_out: {
- value: 0,
- },
- },
- ],
- },
- },
- },
- total: 21,
- loaded: 21,
-};
-
-export const formattedDnsSearchStrategyResponse: MatrixHistogramStrategyResponse = {
- ...mockDnsSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- allow_no_indices: true,
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- ignore_unavailable: true,
- body: {
- aggregations: {
- dns_count: { cardinality: { field: 'dns.question.registered_domain' } },
- dns_name_query_count: {
- terms: {
- field: 'dns.question.registered_domain',
- order: { unique_domains: 'desc' },
- size: 10,
- },
- aggs: {
- unique_domains: { cardinality: { field: 'dns.question.name' } },
- dns_question_name: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599579675528, max: 1599666075529 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T15:41:15.528Z',
- lte: '2020-09-09T15:41:15.529Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- must_not: [{ term: { 'dns.question.type': { value: 'PTR' } } }],
- },
- },
- },
- size: 0,
- track_total_hits: false,
- },
- null,
- 2
- ),
- ],
- },
- matrixHistogramData: [
- { x: 1605143611395, y: 0, g: 'google.com' },
- { x: 1605144108492, y: 0, g: 'google.com' },
- { x: 1605144605589, y: 0, g: 'google.com' },
- { x: 1605145102686, y: 0, g: 'google.com' },
- { x: 1605145599783, y: 0, g: 'google.com' },
- { x: 1605146096880, y: 0, g: 'google.com' },
- { x: 1605146593977, y: 0, g: 'google.com' },
- { x: 1605147091074, y: 0, g: 'google.com' },
- { x: 1605147588171, y: 0, g: 'google.com' },
- { x: 1605148085268, y: 0, g: 'google.com' },
- { x: 1605148582365, y: 0, g: 'google.com' },
- { x: 1605149079462, y: 0, g: 'google.com' },
- { x: 1605149576559, y: 0, g: 'google.com' },
- { x: 1605150073656, y: 0, g: 'google.com' },
- { x: 1605150570753, y: 0, g: 'google.com' },
- { x: 1605151067850, y: 0, g: 'google.com' },
- { x: 1605151564947, y: 0, g: 'google.com' },
- { x: 1605152062044, y: 0, g: 'google.com' },
- { x: 1605152559141, y: 0, g: 'google.com' },
- { x: 1605153056238, y: 1, g: 'google.com' },
- { x: 1605153553335, y: 0, g: 'google.com' },
- { x: 1605154050432, y: 0, g: 'google.com' },
- { x: 1605154547529, y: 0, g: 'google.com' },
- { x: 1605155044626, y: 0, g: 'google.com' },
- { x: 1605155541723, y: 0, g: 'google.com' },
- { x: 1605156038820, y: 0, g: 'google.com' },
- { x: 1605156535917, y: 0, g: 'google.com' },
- { x: 1605157033014, y: 0, g: 'google.com' },
- { x: 1605157530111, y: 0, g: 'google.com' },
- { x: 1605158027208, y: 0, g: 'google.com' },
- { x: 1605158524305, y: 0, g: 'google.com' },
- { x: 1605159021402, y: 0, g: 'google.com' },
- { x: 1605159518499, y: 0, g: 'google.com' },
- { x: 1605143611395, y: 0, g: 'google.internal' },
- { x: 1605144108492, y: 0, g: 'google.internal' },
- { x: 1605144605589, y: 0, g: 'google.internal' },
- { x: 1605145102686, y: 0, g: 'google.internal' },
- { x: 1605145599783, y: 0, g: 'google.internal' },
- { x: 1605146096880, y: 0, g: 'google.internal' },
- { x: 1605146593977, y: 0, g: 'google.internal' },
- { x: 1605147091074, y: 0, g: 'google.internal' },
- { x: 1605147588171, y: 0, g: 'google.internal' },
- { x: 1605148085268, y: 0, g: 'google.internal' },
- { x: 1605148582365, y: 0, g: 'google.internal' },
- { x: 1605149079462, y: 0, g: 'google.internal' },
- { x: 1605149576559, y: 0, g: 'google.internal' },
- { x: 1605150073656, y: 0, g: 'google.internal' },
- { x: 1605150570753, y: 0, g: 'google.internal' },
- { x: 1605151067850, y: 0, g: 'google.internal' },
- { x: 1605151564947, y: 0, g: 'google.internal' },
- { x: 1605152062044, y: 0, g: 'google.internal' },
- { x: 1605152559141, y: 0, g: 'google.internal' },
- { x: 1605153056238, y: 1, g: 'google.internal' },
- { x: 1605153553335, y: 0, g: 'google.internal' },
- { x: 1605154050432, y: 0, g: 'google.internal' },
- { x: 1605154547529, y: 0, g: 'google.internal' },
- { x: 1605155044626, y: 0, g: 'google.internal' },
- { x: 1605155541723, y: 0, g: 'google.internal' },
- { x: 1605156038820, y: 0, g: 'google.internal' },
- { x: 1605156535917, y: 0, g: 'google.internal' },
- { x: 1605157033014, y: 0, g: 'google.internal' },
- { x: 1605157530111, y: 0, g: 'google.internal' },
- { x: 1605158027208, y: 0, g: 'google.internal' },
- { x: 1605158524305, y: 0, g: 'google.internal' },
- { x: 1605159021402, y: 0, g: 'google.internal' },
- { x: 1605159518499, y: 0, g: 'google.internal' },
- { x: 1605143611395, y: 0, g: 'windows.net' },
- { x: 1605144108492, y: 0, g: 'windows.net' },
- { x: 1605144605589, y: 0, g: 'windows.net' },
- { x: 1605145102686, y: 0, g: 'windows.net' },
- { x: 1605145599783, y: 0, g: 'windows.net' },
- { x: 1605146096880, y: 0, g: 'windows.net' },
- { x: 1605146593977, y: 0, g: 'windows.net' },
- { x: 1605147091074, y: 0, g: 'windows.net' },
- { x: 1605147588171, y: 0, g: 'windows.net' },
- { x: 1605148085268, y: 0, g: 'windows.net' },
- { x: 1605148582365, y: 0, g: 'windows.net' },
- { x: 1605149079462, y: 0, g: 'windows.net' },
- { x: 1605149576559, y: 0, g: 'windows.net' },
- { x: 1605150073656, y: 1, g: 'windows.net' },
- { x: 1605150570753, y: 0, g: 'windows.net' },
- { x: 1605151067850, y: 0, g: 'windows.net' },
- { x: 1605151564947, y: 0, g: 'windows.net' },
- { x: 1605152062044, y: 0, g: 'windows.net' },
- { x: 1605152559141, y: 0, g: 'windows.net' },
- { x: 1605153056238, y: 0, g: 'windows.net' },
- { x: 1605153553335, y: 0, g: 'windows.net' },
- { x: 1605154050432, y: 0, g: 'windows.net' },
- { x: 1605154547529, y: 0, g: 'windows.net' },
- { x: 1605155044626, y: 0, g: 'windows.net' },
- { x: 1605155541723, y: 0, g: 'windows.net' },
- { x: 1605156038820, y: 0, g: 'windows.net' },
- { x: 1605156535917, y: 0, g: 'windows.net' },
- { x: 1605157033014, y: 0, g: 'windows.net' },
- { x: 1605157530111, y: 0, g: 'windows.net' },
- { x: 1605158027208, y: 0, g: 'windows.net' },
- { x: 1605158524305, y: 0, g: 'windows.net' },
- { x: 1605159021402, y: 0, g: 'windows.net' },
- { x: 1605159518499, y: 0, g: 'windows.net' },
- ],
- totalCount: 0,
-};
-
-export const formattedPreviewStrategyResponse = {
- ...mockAlertsSearchStrategyResponse,
- inspect: {
- dsl: [
- JSON.stringify(
- {
- index: ['.siem-preview-signals-default'],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- preview: {
- terms: {
- field: 'event.category',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- preview: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599574984482, max: 1599661384482 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- {
- bool: {
- filter: [
- {
- bool: {
- should: [{ match: { 'signal.rule.id': 'test-preview-id' } }],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- ],
- should: [],
- must_not: [],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T14:23:04.482Z',
- lte: '2020-09-09T14:23:04.482Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
- },
- null,
- 2
- ),
- ],
- },
- matrixHistogramData: [],
- totalCount: 0,
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts
deleted file mode 100644
index dda3395b54c1a..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/__mocks__/index.ts
+++ /dev/null
@@ -1,116 +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 { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramQuery } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramType } from '../../../../../../../common/search_strategy';
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- filterQuery:
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"exists":{"field":"host.name"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}',
- histogramType: MatrixHistogramType.alerts,
- timerange: { interval: '12h', from: '2020-09-08T14:23:04.482Z', to: '2020-09-09T14:23:04.482Z' },
- stackByField: 'event.module',
- includeMissingData: false,
- isPtrIncluded: false,
- factoryQueryType: MatrixHistogramQuery,
-};
-
-export const expectedDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- alertsGroup: {
- terms: {
- field: 'event.module',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- alerts: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599574984482, max: 1599661384482 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- {
- bool: {
- filter: [
- {
- bool: {
- should: [{ exists: { field: 'host.name' } }],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- ],
- should: [],
- must_not: [],
- },
- },
- {
- bool: {
- filter: [
- {
- bool: { should: [{ match: { 'event.kind': 'alert' } }], minimum_should_match: 1 },
- },
- ],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T14:23:04.482Z',
- lte: '2020-09-09T14:23:04.482Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts
deleted file mode 100644
index 310161d13d25c..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.test.ts
+++ /dev/null
@@ -1,23 +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 { alertsMatrixHistogramConfig } from '.';
-import { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl';
-
-jest.mock('./query.alerts_histogram.dsl', () => ({
- buildAlertsHistogramQuery: jest.fn(),
-}));
-
-describe('alertsMatrixHistogramConfig', () => {
- test('should export alertsMatrixHistogramConfig corrrectly', () => {
- expect(alertsMatrixHistogramConfig).toEqual({
- aggName: 'aggregations.alertsGroup.buckets',
- parseKey: 'alerts.buckets',
- buildDsl: buildAlertsHistogramQuery,
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.ts
deleted file mode 100644
index 59985a927aa8f..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/index.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 { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-import { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl';
-
-export const alertsMatrixHistogramConfig = {
- buildDsl: buildAlertsHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.alerts,
- parseKey: 'alerts.buckets',
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.ts
deleted file mode 100644
index 19f6dc202824a..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.test.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 { buildAlertsHistogramQuery } from './query.alerts_histogram.dsl';
-import { mockOptions, expectedDsl } from './__mocks__';
-
-describe('buildAlertsHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildAlertsHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts
deleted file mode 100644
index bee0f1ac7f457..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/alerts/query.alerts_histogram.dsl.ts
+++ /dev/null
@@ -1,101 +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 moment from 'moment';
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-
-import {
- createQueryFilterClauses,
- calculateTimeSeriesInterval,
-} from '../../../../../utils/build_query';
-
-export const buildAlertsHistogramQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
- stackByField,
-}: MatrixHistogramRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- bool: {
- filter: [
- {
- bool: {
- should: [
- {
- match: {
- 'event.kind': 'alert',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const getHistogramAggregation = () => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = '@timestamp';
- const dateHistogram = {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
- return {
- alertsGroup: {
- terms: {
- field: stackByField,
- missing: 'All others',
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- alerts: dateHistogram,
- },
- },
- };
- };
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: getHistogramAggregation(),
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts
deleted file mode 100644
index 69b1977f17083..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/__mocks__/index.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramQuery } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramType } from '../../../../../../../common/search_strategy';
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- filterQuery:
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"should":[],"minimum_should_match":1}},{"match_phrase":{"result_type":"record"}},null,{"range":{"record_score":{"gte":50}}}],"should":[{"exists":{"field":"source.ip"}},{"exists":{"field":"destination.ip"}}],"must_not":[],"minimum_should_match":1}}',
- histogramType: MatrixHistogramType.anomalies,
- timerange: { interval: '12h', from: '2020-09-08T15:14:35.566Z', to: '2020-09-09T15:14:35.566Z' },
- stackByField: 'job_id',
- includeMissingData: false,
- isPtrIncluded: false,
- factoryQueryType: MatrixHistogramQuery,
-};
-
-export const expectedDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggs: {
- anomalyActionGroup: {
- terms: { field: 'job_id', order: { _count: 'desc' }, size: 10 },
- aggs: {
- anomalies: {
- date_histogram: {
- field: 'timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599578075566, max: 1599664475566 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- { bool: { should: [], minimum_should_match: 1 } },
- { match_phrase: { result_type: 'record' } },
- null,
- { range: { record_score: { gte: 50 } } },
- ],
- should: [{ exists: { field: 'source.ip' } }, { exists: { field: 'destination.ip' } }],
- must_not: [],
- minimum_should_match: 1,
- },
- },
- {
- range: {
- timestamp: {
- gte: '2020-09-08T15:14:35.566Z',
- lte: '2020-09-09T15:14:35.566Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts
deleted file mode 100644
index 8664d967c5884..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.test.ts
+++ /dev/null
@@ -1,23 +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 { anomaliesMatrixHistogramConfig } from '.';
-import { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl';
-
-jest.mock('./query.anomalies_histogram.dsl', () => ({
- buildAnomaliesHistogramQuery: jest.fn(),
-}));
-
-describe('anomaliesMatrixHistogramConfig', () => {
- test('should export anomaliesMatrixHistogramConfig corrrectly', () => {
- expect(anomaliesMatrixHistogramConfig).toEqual({
- aggName: 'aggregations.anomalyActionGroup.buckets',
- parseKey: 'anomalies.buckets',
- buildDsl: buildAnomaliesHistogramQuery,
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.ts
deleted file mode 100644
index c7e67566acfc2..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/index.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 { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-import { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl';
-
-export const anomaliesMatrixHistogramConfig = {
- buildDsl: buildAnomaliesHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.anomalies,
- parseKey: 'anomalies.buckets',
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.ts
deleted file mode 100644
index ddf3a1f43b1b8..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.test.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 { buildAnomaliesHistogramQuery } from './query.anomalies_histogram.dsl';
-import { mockOptions, expectedDsl } from './__mocks__';
-
-describe('buildAnomaliesHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildAnomaliesHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.ts
deleted file mode 100644
index da1b72341c71a..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/anomalies/query.anomalies_histogram.dsl.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 moment from 'moment';
-
-import {
- createQueryFilterClauses,
- calculateTimeSeriesInterval,
-} from '../../../../../utils/build_query';
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-
-export const buildAnomaliesHistogramQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
- stackByField = 'job_id',
-}: MatrixHistogramRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- timestamp: {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const getHistogramAggregation = () => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = 'timestamp';
- const dateHistogram = {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
- return {
- anomalyActionGroup: {
- terms: {
- field: stackByField,
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- anomalies: dateHistogram,
- },
- },
- };
- };
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggs: getHistogramAggregation(),
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts
deleted file mode 100644
index 0f7145dc95320..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/__mocks__/index.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramQuery } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramType } from '../../../../../../../common/search_strategy';
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}',
- histogramType: MatrixHistogramType.authentications,
- timerange: { interval: '12h', from: '2020-09-08T15:22:00.325Z', to: '2020-09-09T15:22:00.325Z' },
- stackByField: 'event.outcome',
- includeMissingData: false,
- isPtrIncluded: false,
- factoryQueryType: MatrixHistogramQuery,
-};
-
-export const expectedDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'event.outcome',
- include: ['success', 'failure'],
- order: { _count: 'desc' },
- size: 2,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599578520325, max: 1599664920325 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- { bool: { must: [{ term: { 'event.category': 'authentication' } }] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T15:22:00.325Z',
- lte: '2020-09-09T15:22:00.325Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts
deleted file mode 100644
index 553c833adc295..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.test.ts
+++ /dev/null
@@ -1,23 +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 { authenticationsMatrixHistogramConfig } from '.';
-import { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl';
-
-jest.mock('./query.authentications_histogram.dsl', () => ({
- buildAuthenticationsHistogramQuery: jest.fn(),
-}));
-
-describe('authenticationsMatrixHistogramConfig', () => {
- test('should export authenticationsMatrixHistogramConfig corrrectly', () => {
- expect(authenticationsMatrixHistogramConfig).toEqual({
- aggName: 'aggregations.eventActionGroup.buckets',
- parseKey: 'events.buckets',
- buildDsl: buildAuthenticationsHistogramQuery,
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.ts
deleted file mode 100644
index 377cd0a019d3f..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/index.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 { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-import { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl';
-
-export const authenticationsMatrixHistogramConfig = {
- buildDsl: buildAuthenticationsHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.authentications,
- parseKey: 'events.buckets',
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.ts
deleted file mode 100644
index 80e5a64137ae6..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.test.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 { buildAuthenticationsHistogramQuery } from './query.authentications_histogram.dsl';
-import { mockOptions, expectedDsl } from './__mocks__';
-
-describe('buildAuthenticationsHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildAuthenticationsHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts
deleted file mode 100644
index a0bae6ad6d322..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/authentications/query.authentications_histogram.dsl.ts
+++ /dev/null
@@ -1,94 +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 moment from 'moment';
-
-import {
- createQueryFilterClauses,
- calculateTimeSeriesInterval,
-} from '../../../../../utils/build_query';
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-
-export const buildAuthenticationsHistogramQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
- stackByField = 'event.outcome',
-}: MatrixHistogramRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- bool: {
- must: [
- {
- term: {
- 'event.category': 'authentication',
- },
- },
- ],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const getHistogramAggregation = () => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = '@timestamp';
- const dateHistogram = {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
- return {
- eventActionGroup: {
- terms: {
- field: stackByField,
- include: ['success', 'failure'],
- order: {
- _count: 'desc',
- },
- size: 2,
- },
- aggs: {
- events: dateHistogram,
- },
- },
- };
- };
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: getHistogramAggregation(),
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts
deleted file mode 100644
index 5823206d58b63..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/__mocks__/index.ts
+++ /dev/null
@@ -1,87 +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 { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramQuery } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramType } from '../../../../../../../common/search_strategy';
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}',
- histogramType: MatrixHistogramType.dns,
- isPtrIncluded: false,
- timerange: { interval: '12h', from: '2020-09-08T15:41:15.528Z', to: '2020-09-09T15:41:15.529Z' },
- stackByField: 'dns.question.registered_domain',
- includeMissingData: false,
- factoryQueryType: MatrixHistogramQuery,
-};
-
-export const expectedDsl = {
- allow_no_indices: true,
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- ignore_unavailable: true,
- body: {
- aggregations: {
- dns_count: { cardinality: { field: 'dns.question.registered_domain' } },
- dns_name_query_count: {
- terms: {
- field: 'dns.question.registered_domain',
- order: { unique_domains: 'desc' },
- size: 10,
- },
- aggs: {
- unique_domains: { cardinality: { field: 'dns.question.name' } },
- dns_question_name: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599579675528, max: 1599666075529 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T15:41:15.528Z',
- lte: '2020-09-09T15:41:15.529Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- must_not: [{ term: { 'dns.question.type': { value: 'PTR' } } }],
- },
- },
- },
- size: 0,
- track_total_hits: false,
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/helpers.ts
deleted file mode 100644
index 3e5cd71241422..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/helpers.ts
+++ /dev/null
@@ -1,34 +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 { get, getOr } from 'lodash/fp';
-import type {
- MatrixHistogramData,
- MatrixHistogramParseData,
- DnsHistogramSubBucket,
-} from '../../../../../../common/search_strategy/security_solution/matrix_histogram';
-
-export const getDnsParsedData = (
- data: MatrixHistogramParseData,
- keyBucket: string
-): MatrixHistogramData[] => {
- let result: MatrixHistogramData[] = [];
- data.forEach((bucketData: unknown) => {
- const questionName = get('key', bucketData);
- const histData = getOr([], keyBucket, bucketData).map(
- // eslint-disable-next-line @typescript-eslint/naming-convention
- ({ key, doc_count }: DnsHistogramSubBucket) => ({
- x: key,
- y: doc_count,
- g: questionName,
- })
- );
-
- result = [...result, ...histData];
- });
- return result;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts
deleted file mode 100644
index 1ee38fc5e6728..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.test.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { dnsMatrixHistogramConfig } from '.';
-import { buildDnsHistogramQuery } from './query.dns_histogram.dsl';
-import { getDnsParsedData } from './helpers';
-
-jest.mock('./query.dns_histogram.dsl', () => ({
- buildDnsHistogramQuery: jest.fn(),
-}));
-
-jest.mock('./helpers', () => ({
- getDnsParsedData: jest.fn(),
-}));
-
-describe('dnsMatrixHistogramConfig', () => {
- test('should export dnsMatrixHistogramConfig corrrectly', () => {
- expect(dnsMatrixHistogramConfig).toEqual({
- aggName: 'aggregations.dns_name_query_count.buckets',
- parseKey: 'dns_question_name.buckets',
- buildDsl: buildDnsHistogramQuery,
- parser: getDnsParsedData,
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts
deleted file mode 100644
index f52a760db842e..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/index.ts
+++ /dev/null
@@ -1,17 +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 { buildDnsHistogramQuery } from './query.dns_histogram.dsl';
-import { getDnsParsedData } from './helpers';
-import { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-
-export const dnsMatrixHistogramConfig = {
- buildDsl: buildDnsHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.dns,
- parseKey: 'dns_question_name.buckets',
- parser: getDnsParsedData,
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.ts
deleted file mode 100644
index 024bbec987f9f..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.test.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 { buildDnsHistogramQuery } from './query.dns_histogram.dsl';
-import { mockOptions, expectedDsl } from './__mocks__';
-
-describe('buildDnsHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildDnsHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts
deleted file mode 100644
index 825f121c87fb3..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/dns/query.dns_histogram.dsl.ts
+++ /dev/null
@@ -1,113 +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 moment from 'moment';
-
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-import {
- calculateTimeSeriesInterval,
- createQueryFilterClauses,
-} from '../../../../../utils/build_query';
-
-const getCountAgg = () => ({
- dns_count: {
- cardinality: {
- field: 'dns.question.registered_domain',
- },
- },
-});
-
-const createIncludePTRFilter = (isPtrIncluded: boolean) =>
- isPtrIncluded
- ? {}
- : {
- must_not: [
- {
- term: {
- 'dns.question.type': {
- value: 'PTR',
- },
- },
- },
- ],
- };
-
-const getHistogramAggregation = ({ from, to }: { from: string; to: string }) => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = '@timestamp';
-
- return {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
-};
-
-export const buildDnsHistogramQuery = ({
- defaultIndex,
- filterQuery,
- isPtrIncluded = false,
- stackByField = 'dns.question.registered_domain',
- timerange: { from, to },
-}: MatrixHistogramRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- allow_no_indices: true,
- index: defaultIndex,
- ignore_unavailable: true,
- body: {
- aggregations: {
- ...getCountAgg(),
- dns_name_query_count: {
- terms: {
- field: stackByField,
- order: {
- unique_domains: 'desc',
- },
- size: 10,
- },
- aggs: {
- unique_domains: {
- cardinality: {
- field: 'dns.question.name',
- },
- },
- dns_question_name: getHistogramAggregation({ from, to }),
- },
- },
- },
- query: {
- bool: {
- filter,
- ...createIncludePTRFilter(isPtrIncluded),
- },
- },
- },
- size: 0,
- track_total_hits: false,
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts
deleted file mode 100644
index e1b25916ec978..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/__mocks__/index.ts
+++ /dev/null
@@ -1,463 +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 { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import {
- MatrixHistogramQuery,
- MatrixHistogramType,
-} from '../../../../../../../common/search_strategy';
-
-const runtimeMappings: MatrixHistogramRequestOptions['runtimeMappings'] = {
- '@a.runtime.field': {
- script: {
- source: 'emit("Radically mocked dude: " + doc[\'host.name\'].value)',
- },
- type: 'keyword',
- },
-};
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- factoryQueryType: MatrixHistogramQuery,
- filterQuery: '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}',
- histogramType: MatrixHistogramType.events,
- timerange: { interval: '12h', from: '2020-09-08T16:11:26.215Z', to: '2020-09-09T16:11:26.215Z' },
- stackByField: 'event.action',
- runtimeMappings,
- includeMissingData: true,
- isPtrIncluded: false,
-};
-
-export const expectedDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'event.action',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
-};
-
-export const expectedThresholdDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- script: {
- lang: 'painless',
- source: "doc['host.name'].value + ':' + doc['agent.name'].value",
- },
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 200,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
-};
-
-export const expectedThresholdMissingFieldDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'event.action',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 200,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { must: [], filter: [{ match_all: {} }], should: [], must_not: [] } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
-};
-
-export const expectedThresholdWithCardinalityDsl = {
- allow_no_indices: true,
- body: {
- aggregations: {
- eventActionGroup: {
- aggs: {
- cardinality_check: {
- bucket_selector: {
- buckets_path: { cardinalityCount: 'cardinality_count' },
- script: 'params.cardinalityCount >= 10',
- },
- },
- cardinality_count: { cardinality: { field: 'agent.name' } },
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 200,
- },
- },
- },
- terms: {
- field: 'event.action',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } },
- {
- range: {
- '@timestamp': {
- format: 'strict_date_optional_time',
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
- ignore_unavailable: true,
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- track_total_hits: true,
-};
-
-export const expectedThresholdGroupWithCardinalityDsl = {
- allow_no_indices: true,
- body: {
- aggregations: {
- eventActionGroup: {
- aggs: {
- cardinality_check: {
- bucket_selector: {
- buckets_path: { cardinalityCount: 'cardinality_count' },
- script: 'params.cardinalityCount >= 10',
- },
- },
- cardinality_count: { cardinality: { field: 'agent.name' } },
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 200,
- },
- },
- },
- terms: {
- order: { _count: 'desc' },
- script: {
- lang: 'painless',
- source: "doc['host.name'].value + ':' + doc['agent.name'].value",
- },
- size: 10,
- },
- },
- },
- query: {
- bool: {
- filter: [
- { bool: { filter: [{ match_all: {} }], must: [], must_not: [], should: [] } },
- {
- range: {
- '@timestamp': {
- format: 'strict_date_optional_time',
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
- ignore_unavailable: true,
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- track_total_hits: true,
-};
-
-export const expectedIpIncludingMissingDataDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: {
- field: 'source.ip',
- missing: '0.0.0.0',
- value_type: 'ip',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [{ match_all: {} }],
- should: [],
- must_not: [{ exists: { field: 'source.ip' } }],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
-};
-
-export const expectedIpNotIncludingMissingDataDsl = {
- index: [
- 'apm-*-transaction*',
- 'traces-apm*',
- 'auditbeat-*',
- 'endgame-*',
- 'filebeat-*',
- 'logs-*',
- 'packetbeat-*',
- 'winlogbeat-*',
- ],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- eventActionGroup: {
- terms: { field: 'source.ip', order: { _count: 'desc' }, size: 10, value_type: 'ip' },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [{ match_all: {} }],
- should: [],
- must_not: [],
- },
- },
- { exists: { field: 'source.ip' } },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T16:11:26.215Z',
- lte: '2020-09-09T16:11:26.215Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts
deleted file mode 100644
index 477381778a367..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.test.ts
+++ /dev/null
@@ -1,175 +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 { BaseQuery } from './helpers';
-import { buildThresholdTermsQuery, buildThresholdCardinalityQuery } from './helpers';
-
-const BASE_QUERY: BaseQuery = {
- eventActionGroup: {
- terms: {
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '5000ms',
- min_doc_count: 0,
- extended_bounds: {
- min: 1599581486215,
- max: 1599667886215,
- },
- },
- },
- },
- },
-};
-
-const STACK_BY_FIELD = 'event.action';
-
-describe('buildEventsHistogramQuery - helpers', () => {
- describe('buildThresholdTermsQuery', () => {
- test('it builds a terms query using script if threshold field/s exist', () => {
- const query = buildThresholdTermsQuery({
- query: BASE_QUERY,
- fields: ['agent.name', 'host.name'],
- stackByField: STACK_BY_FIELD,
- missing: {},
- });
- expect(query).toEqual({
- eventActionGroup: {
- aggs: {
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '5000ms',
- min_doc_count: 0,
- },
- },
- },
- terms: {
- order: { _count: 'desc' },
- script: {
- lang: 'painless',
- source: "doc['agent.name'].value + ':' + doc['host.name'].value",
- },
- size: 10,
- },
- },
- });
- });
-
- test('it builds a terms query using default stackByField if threshold field/s do not exist', () => {
- const query = buildThresholdTermsQuery({
- query: BASE_QUERY,
- fields: [],
- stackByField: STACK_BY_FIELD,
- missing: { missing: 'All others' },
- });
- expect(query).toEqual({
- eventActionGroup: {
- aggs: {
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '5000ms',
- min_doc_count: 0,
- },
- },
- },
- terms: {
- field: 'event.action',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- },
- });
- });
- });
-
- describe('buildThresholdCardinalityQuery', () => {
- const TERMS_QUERY = {
- eventActionGroup: {
- terms: {
- field: 'host.name',
- order: { _count: 'desc' },
- size: 10,
- min_doc_count: 200,
- },
- aggs: {
- events: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599581486215, max: 1599667886215 },
- },
- },
- },
- },
- };
-
- test('it builds query with cardinality', () => {
- const query = buildThresholdCardinalityQuery({
- query: TERMS_QUERY,
- cardinalityField: 'agent.name',
- cardinalityValue: '100',
- });
- expect(query).toEqual({
- eventActionGroup: {
- aggs: {
- cardinality_check: {
- bucket_selector: {
- buckets_path: { cardinalityCount: 'cardinality_count' },
- script: 'params.cardinalityCount >= 100',
- },
- },
- cardinality_count: { cardinality: { field: 'agent.name' } },
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- },
- },
- },
- terms: { field: 'host.name', min_doc_count: 200, order: { _count: 'desc' }, size: 10 },
- },
- });
- });
-
- test('it builds a terms query using default stackByField if threshold field/s do not exist', () => {
- const query = buildThresholdCardinalityQuery({
- query: TERMS_QUERY,
- cardinalityField: '',
- cardinalityValue: '',
- });
- expect(query).toEqual({
- eventActionGroup: {
- aggs: {
- events: {
- date_histogram: {
- extended_bounds: { max: 1599667886215, min: 1599581486215 },
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- },
- },
- },
- terms: { field: 'host.name', min_doc_count: 200, order: { _count: 'desc' }, size: 10 },
- },
- });
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts
deleted file mode 100644
index 6aed879371a0a..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/helpers.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export interface BaseQuery {
- eventActionGroup: {
- terms: {
- min_doc_count?: number;
- order?: {
- _count?: string;
- };
- size?: number;
- field?: string | string[];
- script?: {
- lang: string;
- source: string;
- };
- missing?: string;
- };
- aggs: {
- events?: unknown;
- cardinality_count?: {
- cardinality?: {
- field?: string;
- };
- };
- cardinality_check?: {
- bucket_selector?: {
- buckets_path?: {
- cardinalityCount?: string;
- };
- script?: string;
- };
- };
- };
- };
-}
-
-export const buildThresholdTermsQuery = ({
- query,
- fields,
- stackByField,
- missing,
-}: {
- query: BaseQuery;
- fields: string[];
- stackByField: string;
- missing: { missing?: string };
-}): BaseQuery => {
- if (fields.length > 1) {
- return {
- eventActionGroup: {
- ...query.eventActionGroup,
- terms: {
- ...query.eventActionGroup.terms,
- script: {
- lang: 'painless',
- source: fields.map((f) => `doc['${f}'].value`).join(` + ':' + `),
- },
- },
- },
- };
- } else {
- return {
- eventActionGroup: {
- ...query.eventActionGroup,
- terms: {
- ...query.eventActionGroup.terms,
- field: fields[0] ?? stackByField,
- ...missing,
- },
- },
- };
- }
-};
-
-export const buildThresholdCardinalityQuery = ({
- query,
- cardinalityField,
- cardinalityValue,
-}: {
- query: BaseQuery;
- cardinalityField: string | undefined;
- cardinalityValue: string;
-}): BaseQuery => {
- if (cardinalityField != null && cardinalityField !== '' && cardinalityValue !== '') {
- return {
- eventActionGroup: {
- ...query.eventActionGroup,
- aggs: {
- ...query.eventActionGroup.aggs,
- cardinality_count: {
- cardinality: {
- field: cardinalityField,
- },
- },
- cardinality_check: {
- bucket_selector: {
- buckets_path: {
- cardinalityCount: 'cardinality_count',
- },
- script: `params.cardinalityCount >= ${cardinalityValue}`,
- },
- },
- },
- },
- };
- } else {
- return query;
- }
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts
deleted file mode 100644
index 6d52262c047d6..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.test.ts
+++ /dev/null
@@ -1,23 +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 { eventsMatrixHistogramConfig } from '.';
-import { buildEventsHistogramQuery } from './query.events_histogram.dsl';
-
-jest.mock('./query.events_histogram.dsl', () => ({
- buildEventsHistogramQuery: jest.fn(),
-}));
-
-describe('eventsMatrixHistogramConfig', () => {
- test('should export eventsMatrixHistogramConfig corrrectly', () => {
- expect(eventsMatrixHistogramConfig).toEqual({
- aggName: 'aggregations.eventActionGroup.buckets',
- parseKey: 'events.buckets',
- buildDsl: buildEventsHistogramQuery,
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.ts
deleted file mode 100644
index 954cd6509e2b0..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/index.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 { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-import { buildEventsHistogramQuery } from './query.events_histogram.dsl';
-
-export const eventsMatrixHistogramConfig = {
- buildDsl: buildEventsHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.events,
- parseKey: 'events.buckets',
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts
deleted file mode 100644
index 472b0eb23b0ee..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.test.ts
+++ /dev/null
@@ -1,90 +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 { buildEventsHistogramQuery } from './query.events_histogram.dsl';
-import {
- mockOptions,
- expectedDsl,
- expectedThresholdDsl,
- expectedThresholdMissingFieldDsl,
- expectedThresholdWithCardinalityDsl,
- expectedThresholdGroupWithCardinalityDsl,
- expectedIpIncludingMissingDataDsl,
- expectedIpNotIncludingMissingDataDsl,
-} from './__mocks__';
-
-describe('buildEventsHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildEventsHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-
- test('builds query with just min doc if "threshold.field" is empty array and "missing" param included', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- threshold: { field: [], value: '200', cardinality: { field: [], value: '0' } },
- })
- ).toEqual(expectedThresholdMissingFieldDsl);
- });
-
- test('builds query with specified threshold fields and without "missing" param if "threshold.field" is multi field', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- threshold: {
- field: ['host.name', 'agent.name'],
- value: '200',
- },
- })
- ).toEqual(expectedThresholdDsl);
- });
-
- test('builds query with specified threshold cardinality if defined', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- threshold: {
- field: [],
- value: '200',
- cardinality: { field: ['agent.name'], value: '10' },
- },
- })
- ).toEqual(expectedThresholdWithCardinalityDsl);
- });
-
- test('builds query with specified threshold group fields and cardinality if defined', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- threshold: {
- field: ['host.name', 'agent.name'],
- value: '200',
- cardinality: { field: ['agent.name'], value: '10' },
- },
- })
- ).toEqual(expectedThresholdGroupWithCardinalityDsl);
- });
-
- test('builds query with stack by ip and including missing data', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- stackByField: 'source.ip',
- })
- ).toEqual(expectedIpIncludingMissingDataDsl);
- });
-
- test('builds query with stack by ip and not including missing data', () => {
- expect(
- buildEventsHistogramQuery({
- ...mockOptions,
- includeMissingData: false,
- stackByField: 'source.ip',
- })
- ).toEqual(expectedIpNotIncludingMissingDataDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts
deleted file mode 100644
index fe09a005770fe..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/query.events_histogram.dsl.ts
+++ /dev/null
@@ -1,173 +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 moment from 'moment';
-
-import { showAllOthersBucket } from '../../../../../../common/constants';
-import {
- createQueryFilterClauses,
- calculateTimeSeriesInterval,
-} from '../../../../../utils/build_query';
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-import * as i18n from './translations';
-import type { BaseQuery } from './helpers';
-import { buildThresholdCardinalityQuery, buildThresholdTermsQuery } from './helpers';
-
-export const buildEventsHistogramQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
- stackByField = 'event.action',
- threshold,
- includeMissingData,
- runtimeMappings,
-}: MatrixHistogramRequestOptions) => {
- const [queryFilterFirstClause, ...queryFilterClauses] = createQueryFilterClauses(filterQuery);
- const stackByIpField =
- stackByField != null &&
- showAllOthersBucket.includes(stackByField) &&
- stackByField.endsWith('.ip');
-
- const filter = [
- ...[
- {
- ...queryFilterFirstClause,
- bool: {
- ...(queryFilterFirstClause.bool || {}),
- must_not: [
- ...(queryFilterFirstClause.bool?.must_not || []),
- ...(stackByIpField && includeMissingData
- ? [
- {
- exists: {
- field: stackByField,
- },
- },
- ]
- : []),
- ],
- },
- },
- ...queryFilterClauses,
- ],
- ...(stackByIpField && !includeMissingData
- ? [
- {
- exists: {
- field: stackByField,
- },
- },
- ]
- : []),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const getHistogramAggregation = () => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = '@timestamp';
- const dateHistogram = {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: threshold != null ? Number(threshold?.value) : 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
-
- const missing =
- stackByField != null && showAllOthersBucket.includes(stackByField)
- ? {
- ...(includeMissingData
- ? stackByField?.endsWith('.ip')
- ? { missing: '0.0.0.0' }
- : { missing: i18n.ALL_OTHERS }
- : {}),
- ...(stackByField?.endsWith('.ip') ? { value_type: 'ip' } : {}),
- }
- : {};
-
- if (threshold != null) {
- const query: BaseQuery = {
- eventActionGroup: {
- terms: {
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- events: dateHistogram,
- },
- },
- };
- const baseQuery = buildThresholdTermsQuery({
- query,
- fields: threshold.field ?? [],
- stackByField,
- missing,
- });
-
- if (threshold.cardinality != null) {
- const enrichedQuery = buildThresholdCardinalityQuery({
- query: baseQuery,
- cardinalityField: threshold.cardinality.field[0],
- cardinalityValue: threshold.cardinality.value,
- });
-
- return enrichedQuery;
- }
-
- return baseQuery;
- }
-
- return {
- eventActionGroup: {
- terms: {
- field: stackByField,
- ...missing,
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- events: dateHistogram,
- },
- },
- };
- };
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: getHistogramAggregation(),
- query: {
- bool: {
- filter,
- },
- },
- runtime_mappings: runtimeMappings,
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/translations.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/translations.ts
deleted file mode 100644
index fd3356d4ac6af..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/events/translations.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 { i18n } from '@kbn/i18n';
-
-export const ALL_OTHERS = i18n.translate(
- 'xpack.securitySolution.detectionEngine.alerts.histogram.allOthersGroupingLabel',
- {
- defaultMessage: 'All others',
- }
-);
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.test.ts
deleted file mode 100644
index 3492f8097f676..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.test.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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { MatrixHistogramType } from '../../../../../common/search_strategy';
-import { getGenericData } from './helpers';
-import { stackedByBooleanField, stackedByTextField, result, textResult } from './mock_data';
-
-describe('getGenericData', () => {
- test('stack by a boolean field', () => {
- const res = getGenericData(stackedByBooleanField, 'events.bucket');
- expect(res).toEqual(result);
- });
-
- test('stack by a text field', () => {
- const res = getGenericData(stackedByTextField, 'events.bucket');
- expect(res).toEqual(textResult);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.ts
deleted file mode 100644
index 7e0950d746b4d..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/helpers.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { get, getOr } from 'lodash/fp';
-import type {
- MatrixHistogramParseData,
- MatrixHistogramBucket,
- MatrixHistogramData,
-} from '../../../../../common/search_strategy/security_solution/matrix_histogram';
-
-export const getGenericData = (
- data: MatrixHistogramParseData,
- keyBucket: string
-): MatrixHistogramData[] => {
- let result: MatrixHistogramData[] = [];
- data.forEach((bucketData: unknown) => {
- // if key_as_string is present use it, else default to the existing key
- const group = get('key_as_string', bucketData) ?? get('key', bucketData);
- const histData = getOr([], keyBucket, bucketData).map(
- // eslint-disable-next-line @typescript-eslint/naming-convention
- ({ key, doc_count }: MatrixHistogramBucket) => ({
- x: key,
- y: doc_count,
- g: group,
- })
- );
- result = [...result, ...histData];
- });
-
- return result;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts
deleted file mode 100644
index 4b09dcf1971d9..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.test.ts
+++ /dev/null
@@ -1,249 +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 { MatrixHistogramType } from '../../../../../common/search_strategy/security_solution';
-import { matrixHistogram } from '.';
-import {
- formattedAlertsSearchStrategyResponse,
- formattedAnomaliesSearchStrategyResponse,
- formattedAuthenticationsSearchStrategyResponse,
- formattedEventsSearchStrategyResponse,
- formattedDnsSearchStrategyResponse,
- mockAlertsSearchStrategyResponse,
- mockAnomaliesSearchStrategyResponse,
- mockAuthenticationsSearchStrategyResponse,
- mockEventsSearchStrategyResponse,
- mockDnsSearchStrategyResponse,
- formattedPreviewStrategyResponse,
-} from './__mocks__';
-import { alertsMatrixHistogramConfig } from './alerts';
-import { anomaliesMatrixHistogramConfig } from './anomalies';
-import { authenticationsMatrixHistogramConfig } from './authentications';
-import { eventsMatrixHistogramConfig } from './events';
-import { dnsMatrixHistogramConfig } from './dns';
-import { previewMatrixHistogramConfig } from './preview';
-
-import { mockOptions as mockAlertsOptions } from './alerts/__mocks__';
-import { mockOptions as mockAnomaliesOptions } from './anomalies/__mocks__';
-import { mockOptions as mockAuthenticationsOptions } from './authentications/__mocks__';
-import { mockOptions as mockEventsOptions } from './events/__mocks__';
-import { mockOptions as mockDnsOptions } from './dns/__mocks__';
-import { mockOptions as mockPreviewOptions } from './preview/__mocks__';
-import type { MatrixHistogramRequestOptions } from '../../../../../common/api/search_strategy/matrix_histogram/matrix_histogram';
-
-describe('Alerts matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(alertsMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockAlertsOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAlertsOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions: MatrixHistogramRequestOptions = {
- ...mockAlertsOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(
- mockAlertsOptions,
- mockAlertsSearchStrategyResponse
- );
- expect(result).toMatchObject(formattedAlertsSearchStrategyResponse);
- });
- });
-});
-
-describe('Anomalies matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(anomaliesMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockAnomaliesOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAnomaliesOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions: MatrixHistogramRequestOptions = {
- ...mockAnomaliesOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(
- mockAnomaliesOptions,
- mockAnomaliesSearchStrategyResponse
- );
- expect(result).toMatchObject(formattedAnomaliesSearchStrategyResponse);
- });
- });
-});
-
-describe('Authentications matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(authenticationsMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockAuthenticationsOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockAuthenticationsOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions = {
- ...mockAuthenticationsOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(
- mockAuthenticationsOptions,
- mockAuthenticationsSearchStrategyResponse
- );
- expect(result).toMatchObject(formattedAuthenticationsSearchStrategyResponse);
- });
- });
-});
-
-describe('Events matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(eventsMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockEventsOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockEventsOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions = {
- ...mockEventsOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(
- mockEventsOptions,
- mockEventsSearchStrategyResponse
- );
- expect(result).toMatchObject(formattedEventsSearchStrategyResponse);
- });
- });
-});
-
-describe('Dns matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(dnsMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockDnsOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockDnsOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions = {
- ...mockDnsOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(mockDnsOptions, mockDnsSearchStrategyResponse);
- expect(result).toMatchObject(formattedDnsSearchStrategyResponse);
- });
- });
-});
-
-describe('Preview matrixHistogram search strategy', () => {
- const buildMatrixHistogramQuery = jest.spyOn(previewMatrixHistogramConfig, 'buildDsl');
-
- afterEach(() => {
- buildMatrixHistogramQuery.mockClear();
- });
-
- describe('buildDsl', () => {
- test('should build dsl query', () => {
- matrixHistogram.buildDsl(mockPreviewOptions);
- expect(buildMatrixHistogramQuery).toHaveBeenCalledWith(mockPreviewOptions);
- });
-
- test('should throw error if histogramType is invalid', () => {
- const invalidOptions: MatrixHistogramRequestOptions = {
- ...mockPreviewOptions,
- histogramType: 'xxx' as MatrixHistogramType,
- } as MatrixHistogramRequestOptions;
-
- expect(() => {
- matrixHistogram.buildDsl(invalidOptions);
- }).toThrowError(/This histogram type xxx is unknown to the server side/);
- });
- });
-
- describe('parse', () => {
- test('should parse data correctly', async () => {
- const result = await matrixHistogram.parse(
- mockPreviewOptions,
- mockAlertsSearchStrategyResponse
- );
- expect(result).toMatchObject(formattedPreviewStrategyResponse);
- });
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.ts
deleted file mode 100644
index 567bdcbd1f78b..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/index.ts
+++ /dev/null
@@ -1,80 +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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- FactoryQueryTypes,
- MatrixHistogramStrategyResponse,
- MatrixHistogramDataConfig,
-} from '../../../../../common/search_strategy/security_solution';
-import {
- MatrixHistogramQuery,
- MatrixHistogramType,
-} from '../../../../../common/search_strategy/security_solution';
-import { inspectStringifyObject } from '../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../types';
-import { getGenericData } from './helpers';
-import { alertsMatrixHistogramConfig } from './alerts';
-import { anomaliesMatrixHistogramConfig } from './anomalies';
-import { authenticationsMatrixHistogramConfig } from './authentications';
-import { dnsMatrixHistogramConfig } from './dns';
-import { eventsMatrixHistogramConfig } from './events';
-import { previewMatrixHistogramConfig } from './preview';
-
-const matrixHistogramConfig: MatrixHistogramDataConfig = {
- [MatrixHistogramType.alerts]: alertsMatrixHistogramConfig,
- [MatrixHistogramType.anomalies]: anomaliesMatrixHistogramConfig,
- [MatrixHistogramType.authentications]: authenticationsMatrixHistogramConfig,
- [MatrixHistogramType.dns]: dnsMatrixHistogramConfig,
- [MatrixHistogramType.events]: eventsMatrixHistogramConfig,
- [MatrixHistogramType.preview]: previewMatrixHistogramConfig,
-};
-
-export const matrixHistogram: SecuritySolutionFactory = {
- buildDsl: (options) => {
- const myConfig = getOr(null, options.histogramType, matrixHistogramConfig);
- if (myConfig == null) {
- throw new Error(`This histogram type ${options.histogramType} is unknown to the server side`);
- }
- return myConfig.buildDsl(options);
- },
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const myConfig = getOr(null, options.histogramType, matrixHistogramConfig);
- if (myConfig == null) {
- throw new Error(`This histogram type ${options.histogramType} is unknown to the server side`);
- }
- const totalCount = response.rawResponse.hits.total || 0;
- const matrixHistogramData = getOr([], myConfig.aggName, response.rawResponse);
- const inspect = {
- dsl: [inspectStringifyObject(myConfig.buildDsl(options))],
- };
- const dataParser = myConfig.parser ?? getGenericData;
-
- return {
- ...response,
- inspect,
- matrixHistogramData: dataParser(
- matrixHistogramData,
- myConfig.parseKey
- ),
- // @ts-expect-error code doesn't handle TotalHits
- totalCount,
- };
- },
-};
-
-export const matrixHistogramFactory: Record<
- typeof MatrixHistogramQuery,
- SecuritySolutionFactory
-> = {
- [MatrixHistogramQuery]: matrixHistogram,
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/mock_data.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/mock_data.ts
deleted file mode 100644
index 9a938846826a1..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/mock_data.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-export const stackedByBooleanField = [
- {
- key: 1,
- key_as_string: 'true',
- doc_count: 7125,
- events: {
- bucket: [
- { key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
- { key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 774 },
- { key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 415 },
- ],
- },
- },
-];
-export const result = [
- { x: 1652196888075, y: 0, g: 'true' },
- { x: 1652199588074, y: 774, g: 'true' },
- { x: 1652202288073, y: 415, g: 'true' },
-];
-
-export const stackedByTextField = [
- {
- key: 'MacBook-Pro.local',
- doc_count: 7103,
- events: {
- bucket: [
- { key_as_string: '2022-05-10T15:34:48.075Z', key: 1652196888075, doc_count: 0 },
- { key_as_string: '2022-05-10T16:19:48.074Z', key: 1652199588074, doc_count: 774 },
- { key_as_string: '2022-05-10T17:04:48.073Z', key: 1652202288073, doc_count: 415 },
- ],
- },
- },
-];
-
-export const textResult = [
- { x: 1652196888075, y: 0, g: 'MacBook-Pro.local' },
- { x: 1652199588074, y: 774, g: 'MacBook-Pro.local' },
- { x: 1652202288073, y: 415, g: 'MacBook-Pro.local' },
-];
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts
deleted file mode 100644
index 4b7ef376b8cd3..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/__mocks__/index.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { MatrixHistogramRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramQuery } from '../../../../../../../common/api/search_strategy';
-import { MatrixHistogramType } from '../../../../../../../common/search_strategy';
-
-export const mockOptions: MatrixHistogramRequestOptions = {
- defaultIndex: ['.siem-preview-signals-default'],
- filterQuery:
- '{"bool":{"must":[],"filter":[{"match_all":{}},{"bool":{"filter":[{"bool":{"should":[{"match":{"signal.rule.id":"test-preview-id"}}],"minimum_should_match":1}}]}}],"should":[],"must_not":[]}}',
- histogramType: MatrixHistogramType.preview,
- timerange: { interval: '12h', from: '2020-09-08T14:23:04.482Z', to: '2020-09-09T14:23:04.482Z' },
- stackByField: 'event.category',
- includeMissingData: false,
- isPtrIncluded: false,
- factoryQueryType: MatrixHistogramQuery,
-};
-
-export const expectedDsl = {
- index: ['.siem-preview-signals-default'],
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: {
- preview: {
- terms: {
- field: 'event.category',
- missing: 'All others',
- order: { _count: 'desc' },
- size: 10,
- },
- aggs: {
- preview: {
- date_histogram: {
- field: '@timestamp',
- fixed_interval: '2700000ms',
- min_doc_count: 0,
- extended_bounds: { min: 1599574984482, max: 1599661384482 },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter: [
- {
- bool: {
- must: [],
- filter: [
- { match_all: {} },
- {
- bool: {
- filter: [
- {
- bool: {
- should: [{ match: { 'signal.rule.id': 'test-preview-id' } }],
- minimum_should_match: 1,
- },
- },
- ],
- },
- },
- ],
- should: [],
- must_not: [],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: '2020-09-08T14:23:04.482Z',
- lte: '2020-09-09T14:23:04.482Z',
- format: 'strict_date_optional_time',
- },
- },
- },
- ],
- },
- },
- size: 0,
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.ts
deleted file mode 100644
index 0ca14c5489c56..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/index.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 { MatrixHistogramTypeToAggName } from '../../../../../../common/search_strategy';
-import { buildPreviewHistogramQuery } from './query.preview_histogram.dsl';
-
-export const previewMatrixHistogramConfig = {
- buildDsl: buildPreviewHistogramQuery,
- aggName: MatrixHistogramTypeToAggName.preview,
- parseKey: 'preview.buckets',
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.test.ts
deleted file mode 100644
index e12142e21a156..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.test.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 { buildPreviewHistogramQuery } from './query.preview_histogram.dsl';
-import { mockOptions, expectedDsl } from './__mocks__';
-
-describe('buildAlertsHistogramQuery', () => {
- test('build query from options correctly', () => {
- expect(buildPreviewHistogramQuery(mockOptions)).toEqual(expectedDsl);
- });
-});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.ts
deleted file mode 100644
index 56f1ce122bfd0..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/matrix_histogram/preview/query.preview_histogram.dsl.ts
+++ /dev/null
@@ -1,84 +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 moment from 'moment';
-
-import { TIMESTAMP } from '@kbn/rule-data-utils';
-import {
- createQueryFilterClauses,
- calculateTimeSeriesInterval,
-} from '../../../../../utils/build_query';
-import type { MatrixHistogramRequestOptions } from '../../../../../../common/api/search_strategy';
-
-export const buildPreviewHistogramQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
- stackByField,
-}: MatrixHistogramRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- [TIMESTAMP]: {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const getHistogramAggregation = () => {
- const interval = calculateTimeSeriesInterval(from, to);
- const histogramTimestampField = TIMESTAMP;
- const dateHistogram = {
- date_histogram: {
- field: histogramTimestampField,
- fixed_interval: interval,
- min_doc_count: 0,
- extended_bounds: {
- min: moment(from).valueOf(),
- max: moment(to).valueOf(),
- },
- },
- };
- return {
- preview: {
- terms: {
- field: stackByField,
- missing: 'All others',
- order: {
- _count: 'desc',
- },
- size: 10,
- },
- aggs: {
- preview: dateHistogram,
- },
- },
- };
- };
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- aggregations: getHistogramAggregation(),
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.test.ts
index d3621ef22bf2a..82edc9d89156b 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.test.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.test.ts
@@ -5,10 +5,7 @@
* 2.0.
*/
-import {
- NetworkQueries,
- NetworkKpiQueries,
-} from '../../../../../common/search_strategy/security_solution';
+import { NetworkQueries } from '../../../../../common/search_strategy/security_solution';
import { networkFactory } from '.';
import { networkDetails } from './details';
@@ -19,11 +16,6 @@ import { networkTls } from './tls';
import { networkTopCountries } from './top_countries';
import { networkTopNFlow } from './top_n_flow';
import { networkUsers } from './users';
-import { networkKpiDns } from './kpi/dns';
-import { networkKpiNetworkEvents } from './kpi/network_events';
-import { networkKpiTlsHandshakes } from './kpi/tls_handshakes';
-import { networkKpiUniqueFlows } from './kpi/unique_flows';
-import { networkKpiUniquePrivateIps } from './kpi/unique_private_ips';
jest.mock('./details');
jest.mock('./dns');
@@ -33,11 +25,6 @@ jest.mock('./tls');
jest.mock('./top_countries');
jest.mock('./top_n_flow');
jest.mock('./users');
-jest.mock('./kpi/dns');
-jest.mock('./kpi/network_events');
-jest.mock('./kpi/tls_handshakes');
-jest.mock('./kpi/unique_flows');
-jest.mock('./kpi/unique_private_ips');
describe('networkFactory', () => {
test('should include correct apis', () => {
@@ -50,11 +37,6 @@ describe('networkFactory', () => {
[NetworkQueries.topCountries]: networkTopCountries,
[NetworkQueries.topNFlow]: networkTopNFlow,
[NetworkQueries.users]: networkUsers,
- [NetworkKpiQueries.dns]: networkKpiDns,
- [NetworkKpiQueries.networkEvents]: networkKpiNetworkEvents,
- [NetworkKpiQueries.tlsHandshakes]: networkKpiTlsHandshakes,
- [NetworkKpiQueries.uniqueFlows]: networkKpiUniqueFlows,
- [NetworkKpiQueries.uniquePrivateIps]: networkKpiUniquePrivateIps,
};
expect(networkFactory).toEqual(expectedNetworkFactory);
});
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.ts
index 71a671efcdfef..6c32d9b5d51eb 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/index.ts
@@ -5,16 +5,8 @@
* 2.0.
*/
-import {
- NetworkQueries,
- NetworkKpiQueries,
-} from '../../../../../common/search_strategy/security_solution';
+import { NetworkQueries } from '../../../../../common/search_strategy/security_solution';
-import { networkKpiDns } from './kpi/dns';
-import { networkKpiNetworkEvents } from './kpi/network_events';
-import { networkKpiTlsHandshakes } from './kpi/tls_handshakes';
-import { networkKpiUniqueFlows } from './kpi/unique_flows';
-import { networkKpiUniquePrivateIps } from './kpi/unique_private_ips';
import { networkDetails } from './details';
import { networkDns } from './dns';
import { networkHttp } from './http';
@@ -26,7 +18,7 @@ import { networkUsers } from './users';
// TODO: add safer type for the strategy map
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export const networkFactory: Record = {
+export const networkFactory: Record = {
[NetworkQueries.details]: networkDetails,
[NetworkQueries.dns]: networkDns,
[NetworkQueries.http]: networkHttp,
@@ -36,9 +28,4 @@ export const networkFactory: Record = {
[NetworkQueries.topNFlowCount]: networkTopNFlowCount,
[NetworkQueries.topNFlow]: networkTopNFlow,
[NetworkQueries.users]: networkUsers,
- [NetworkKpiQueries.dns]: networkKpiDns,
- [NetworkKpiQueries.networkEvents]: networkKpiNetworkEvents,
- [NetworkKpiQueries.tlsHandshakes]: networkKpiTlsHandshakes,
- [NetworkKpiQueries.uniqueFlows]: networkKpiUniqueFlows,
- [NetworkKpiQueries.uniquePrivateIps]: networkKpiUniquePrivateIps,
};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/common/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/common/index.ts
deleted file mode 100644
index c0d681e60a442..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/common/index.ts
+++ /dev/null
@@ -1,39 +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 { getOr } from 'lodash/fp';
-import type { NetworkKpiHistogramData } from '../../../../../../../common/search_strategy/security_solution/network';
-
-export const getIpFilter = () => [
- {
- bool: {
- should: [
- {
- exists: {
- field: 'source.ip',
- },
- },
- {
- exists: {
- field: 'destination.ip',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
-];
-
-export const formatHistogramData = (
- data: Array<{ key: number; count: { value: number } }>
-): NetworkKpiHistogramData[] | null =>
- data && data.length > 0
- ? data.map(({ key, count }) => ({
- x: key,
- y: getOr(null, 'value', count),
- }))
- : null;
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/index.ts
deleted file mode 100644
index 09dcc714b444c..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/index.ts
+++ /dev/null
@@ -1,34 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- NetworkKpiQueries,
- NetworkKpiDnsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/network';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildDnsQuery } from './query.network_kpi_dns.dsl';
-
-export const networkKpiDns: SecuritySolutionFactory = {
- buildDsl: (options) => buildDnsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildDnsQuery(options))],
- };
-
- return {
- ...response,
- inspect,
- // @ts-expect-error code doesn't handle TotalHits
- dnsQueries: response.rawResponse.hits.total,
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts
deleted file mode 100644
index a55cb8f026664..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/dns/query.network_kpi_dns.dsl.ts
+++ /dev/null
@@ -1,74 +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 { NetworkKpiDnsRequestOptions } from '../../../../../../../common/api/search_strategy';
-
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-const getDnsQueryFilter = () => [
- {
- bool: {
- should: [
- {
- exists: {
- field: 'dns.question.name',
- },
- },
- {
- term: {
- 'suricata.eve.dns.type': {
- value: 'query',
- },
- },
- },
- {
- exists: {
- field: 'zeek.dns.query',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
-];
-
-export const buildDnsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: NetworkKpiDnsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- ...getDnsQueryFilter(),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/index.ts
deleted file mode 100644
index 6751c829cc350..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/index.ts
+++ /dev/null
@@ -1,34 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- NetworkKpiQueries,
- NetworkKpiNetworkEventsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/network';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildNetworkEventsQuery } from './query.network_kpi_network_events.dsl';
-
-export const networkKpiNetworkEvents: SecuritySolutionFactory = {
- buildDsl: (options) => buildNetworkEventsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildNetworkEventsQuery(options))],
- };
-
- return {
- ...response,
- inspect,
- // @ts-expect-error code doesn't handle TotalHits
- networkEvents: response.rawResponse.hits.total,
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts
deleted file mode 100644
index 96f67b8e17600..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/network_events/query.network_kpi_network_events.dsl.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { NetworkKpiEventsRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-import { getIpFilter } from '../common';
-
-export const buildNetworkEventsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: NetworkKpiEventsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- ...getIpFilter(),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/index.ts
deleted file mode 100644
index 3b8e65fb163af..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/index.ts
+++ /dev/null
@@ -1,34 +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 { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- NetworkKpiQueries,
- NetworkKpiTlsHandshakesStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/network';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildTlsHandshakeQuery } from './query.network_kpi_tls_handshakes.dsl';
-
-export const networkKpiTlsHandshakes: SecuritySolutionFactory = {
- buildDsl: (options) => buildTlsHandshakeQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildTlsHandshakeQuery(options))],
- };
-
- return {
- ...response,
- inspect,
- // @ts-expect-error code doesn't handle TotalHits
- tlsHandshakes: response.rawResponse.hits.total,
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts
deleted file mode 100644
index 2797a2e814506..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/tls_handshakes/query.network_kpi_tls_handshakes.dsl.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import type { NetworkKpiTlsHandshakesRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-import { getIpFilter } from '../common';
-
-const getTlsHandshakesQueryFilter = () => [
- {
- bool: {
- should: [
- {
- exists: {
- field: 'tls.version',
- },
- },
- {
- exists: {
- field: 'suricata.eve.tls.version',
- },
- },
- {
- exists: {
- field: 'zeek.ssl.version',
- },
- },
- ],
- minimum_should_match: 1,
- },
- },
-];
-
-export const buildTlsHandshakeQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: NetworkKpiTlsHandshakesRequestOptions) => {
- const filter = [
- ...getIpFilter(),
- ...createQueryFilterClauses(filterQuery),
- ...getTlsHandshakesQueryFilter(),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: true,
- body: {
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/index.ts
deleted file mode 100644
index e245d1af846f8..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/index.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- NetworkKpiQueries,
- NetworkKpiUniqueFlowsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/network';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildUniqueFlowsQuery } from './query.network_kpi_unique_flows.dsl';
-
-export const networkKpiUniqueFlows: SecuritySolutionFactory = {
- buildDsl: (options) => buildUniqueFlowsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildUniqueFlowsQuery(options))],
- };
-
- return {
- ...response,
- inspect,
- uniqueFlowId: getOr(null, 'aggregations.unique_flow_id.value', response.rawResponse),
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts
deleted file mode 100644
index 29e4c386fc348..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_flows/query.network_kpi_unique_flows.dsl.ts
+++ /dev/null
@@ -1,55 +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 { NetworkKpiUniqueFlowsRequestOptions } from '../../../../../../../common/api/search_strategy';
-
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-import { getIpFilter } from '../common';
-
-export const buildUniqueFlowsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: NetworkKpiUniqueFlowsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- ...getIpFilter(),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggregations: {
- unique_flow_id: {
- cardinality: {
- field: 'network.community_id',
- },
- },
- },
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/index.ts
deleted file mode 100644
index a3d72e6e15898..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/index.ts
+++ /dev/null
@@ -1,62 +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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type {
- NetworkKpiQueries,
- NetworkKpiUniquePrivateIpsStrategyResponse,
-} from '../../../../../../../common/search_strategy/security_solution/network';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { formatHistogramData } from '../common';
-import { buildUniquePrivateIpsQuery } from './query.network_kpi_unique_private_ips.dsl';
-
-export const networkKpiUniquePrivateIps: SecuritySolutionFactory =
- {
- // @ts-expect-error auto_date_histogram.buckets is incompatible
- buildDsl: (options) => buildUniquePrivateIpsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildUniquePrivateIpsQuery(options))],
- };
-
- const uniqueSourcePrivateIpsHistogram = getOr(
- null,
- 'aggregations.source.histogram.buckets',
- response.rawResponse
- );
- const uniqueDestinationPrivateIpsHistogram = getOr(
- null,
- 'aggregations.destination.histogram.buckets',
- response.rawResponse
- );
-
- return {
- ...response,
- inspect,
- uniqueSourcePrivateIps: getOr(
- null,
- 'aggregations.source.unique_private_ips.value',
- response.rawResponse
- ),
- uniqueDestinationPrivateIps: getOr(
- null,
- 'aggregations.destination.unique_private_ips.value',
- response.rawResponse
- ),
- uniqueSourcePrivateIpsHistogram: formatHistogramData(uniqueSourcePrivateIpsHistogram),
- uniqueDestinationPrivateIpsHistogram: formatHistogramData(
- uniqueDestinationPrivateIpsHistogram
- ),
- };
- },
- };
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts
deleted file mode 100644
index 6b31e5af05797..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/network/kpi/unique_private_ips/query.network_kpi_unique_private_ips.dsl.ts
+++ /dev/null
@@ -1,104 +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 { NetworkKpiUniquePrivateIpsRequestOptions } from '../../../../../../../common/api/search_strategy';
-import type { UniquePrivateAttributeQuery } from '../../../../../../../common/search_strategy/security_solution/network';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-const getUniquePrivateIpsFilter = (attrQuery: UniquePrivateAttributeQuery) => ({
- bool: {
- should: [
- {
- term: {
- [`${attrQuery}.ip`]: '10.0.0.0/8',
- },
- },
- {
- term: {
- [`${attrQuery}.ip`]: '192.168.0.0/16',
- },
- },
- {
- term: {
- [`${attrQuery}.ip`]: '172.16.0.0/12',
- },
- },
- {
- term: {
- [`${attrQuery}.ip`]: 'fd00::/8',
- },
- },
- ],
- minimum_should_match: 1,
- },
-});
-
-const getAggs = (attrQuery: 'source' | 'destination') => ({
- [attrQuery]: {
- filter: getUniquePrivateIpsFilter(attrQuery),
- aggs: {
- unique_private_ips: {
- cardinality: {
- field: `${attrQuery}.ip`,
- },
- },
- histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: '6',
- },
- aggs: {
- count: {
- cardinality: {
- field: `${attrQuery}.ip`,
- },
- },
- },
- },
- },
- },
-});
-
-export const buildUniquePrivateIpsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: NetworkKpiUniquePrivateIpsRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- allow_no_indices: true,
- index: defaultIndex,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggregations: {
- ...getAggs('source'),
- ...getAggs('destination'),
- },
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/index.ts
index ea4038e53730d..6c3df1aef56a6 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/index.ts
@@ -12,15 +12,12 @@ import type { SecuritySolutionFactory } from '../types';
import { allUsers } from './all';
import { authentications } from './authentications';
import { managedUserDetails } from './managed_details';
-import { usersKpiAuthentications } from './kpi/authentications';
-import { totalUsersKpi } from './kpi/total_users';
+
import { observedUserDetails } from './observed_details';
export const usersFactory: Record> = {
[UsersQueries.observedDetails]: observedUserDetails,
[UsersQueries.managedDetails]: managedUserDetails,
- [UsersQueries.kpiTotalUsers]: totalUsersKpi,
[UsersQueries.users]: allUsers,
[UsersQueries.authentications]: authentications,
- [UsersQueries.kpiAuthentications]: usersKpiAuthentications,
};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/index.ts
deleted file mode 100644
index d8b8d3ba827f2..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/index.ts
+++ /dev/null
@@ -1,58 +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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildUsersKpiAuthenticationsQuery } from './query.users_kpi_authentications.dsl';
-import type {
- UsersKpiAuthenticationsStrategyResponse,
- UsersQueries,
-} from '../../../../../../../common/search_strategy';
-import { formatGeneralHistogramData } from '../../../common/format_general_histogram_data';
-
-export const usersKpiAuthentications: SecuritySolutionFactory = {
- buildDsl: (options) => buildUsersKpiAuthenticationsQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildUsersKpiAuthenticationsQuery(options))],
- };
-
- const authenticationsSuccessHistogram = getOr(
- null,
- 'aggregations.authentication_success_histogram.buckets',
- response.rawResponse
- );
- const authenticationsFailureHistogram = getOr(
- null,
- 'aggregations.authentication_failure_histogram.buckets',
- response.rawResponse
- );
-
- return {
- ...response,
- inspect,
- authenticationsSuccess: getOr(
- null,
- 'aggregations.authentication_success.doc_count',
- response.rawResponse
- ),
- authenticationsSuccessHistogram: formatGeneralHistogramData(authenticationsSuccessHistogram),
- authenticationsFailure: getOr(
- null,
- 'aggregations.authentication_failure.doc_count',
- response.rawResponse
- ),
- authenticationsFailureHistogram: formatGeneralHistogramData(authenticationsFailureHistogram),
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/query.users_kpi_authentications.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/query.users_kpi_authentications.dsl.ts
deleted file mode 100644
index e3e5af4a32a5e..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/authentications/query.users_kpi_authentications.dsl.ts
+++ /dev/null
@@ -1,111 +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 { AuthenticationsKpiRequestOptions } from '../../../../../../../common/api/search_strategy/users/kpi/authentications';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-export const buildUsersKpiAuthenticationsQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: AuthenticationsKpiRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- bool: {
- filter: [
- {
- term: {
- 'event.category': 'authentication',
- },
- },
- ],
- },
- },
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggs: {
- authentication_success: {
- filter: {
- term: {
- 'event.outcome': 'success',
- },
- },
- },
- authentication_success_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- filter: {
- term: {
- 'event.outcome': 'success',
- },
- },
- },
- },
- },
- authentication_failure: {
- filter: {
- term: {
- 'event.outcome': 'failure',
- },
- },
- },
- authentication_failure_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- filter: {
- term: {
- 'event.outcome': 'failure',
- },
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- _source: false,
- fields: [
- 'event.outcome',
- 'event.category',
- {
- field: '@timestamp',
- format: 'strict_date_optional_time',
- },
- ],
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/index.ts
deleted file mode 100644
index 7086b1c6f4f45..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/index.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.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 { getOr } from 'lodash/fp';
-
-import type { IEsSearchResponse } from '@kbn/data-plugin/common';
-import type { UsersQueries } from '../../../../../../../common/search_strategy/security_solution/users';
-import type { TotalUsersKpiStrategyResponse } from '../../../../../../../common/search_strategy/security_solution/users/kpi/total_users';
-
-import { inspectStringifyObject } from '../../../../../../utils/build_query';
-import type { SecuritySolutionFactory } from '../../../types';
-import { buildTotalUsersKpiQuery } from './query.build_total_users_kpi.dsl';
-import { formatGeneralHistogramData } from '../../../common/format_general_histogram_data';
-
-export const totalUsersKpi: SecuritySolutionFactory = {
- buildDsl: (options) => buildTotalUsersKpiQuery(options),
- parse: async (
- options,
- response: IEsSearchResponse
- ): Promise => {
- const inspect = {
- dsl: [inspectStringifyObject(buildTotalUsersKpiQuery(options))],
- };
-
- const usersHistogram = getOr(
- null,
- 'aggregations.users_histogram.buckets',
- response.rawResponse
- );
- return {
- ...response,
- inspect,
- users: getOr(null, 'aggregations.users.value', response.rawResponse),
- usersHistogram: formatGeneralHistogramData(usersHistogram),
- };
- },
-};
diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/query.build_total_users_kpi.dsl.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/query.build_total_users_kpi.dsl.ts
deleted file mode 100644
index 9ff10bd22cb1f..0000000000000
--- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/kpi/total_users/query.build_total_users_kpi.dsl.ts
+++ /dev/null
@@ -1,65 +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 { TotalUsersKpiRequestOptions } from '../../../../../../../common/api/search_strategy';
-import { createQueryFilterClauses } from '../../../../../../utils/build_query';
-
-export const buildTotalUsersKpiQuery = ({
- filterQuery,
- timerange: { from, to },
- defaultIndex,
-}: TotalUsersKpiRequestOptions) => {
- const filter = [
- ...createQueryFilterClauses(filterQuery),
- {
- range: {
- '@timestamp': {
- gte: from,
- lte: to,
- format: 'strict_date_optional_time',
- },
- },
- },
- ];
-
- const dslQuery = {
- index: defaultIndex,
- allow_no_indices: true,
- ignore_unavailable: true,
- track_total_hits: false,
- body: {
- aggregations: {
- users: {
- cardinality: {
- field: 'user.name',
- },
- },
- users_histogram: {
- auto_date_histogram: {
- field: '@timestamp',
- buckets: 6,
- },
- aggs: {
- count: {
- cardinality: {
- field: 'user.name',
- },
- },
- },
- },
- },
- query: {
- bool: {
- filter,
- },
- },
- size: 0,
- },
- };
-
- return dslQuery;
-};
diff --git a/x-pack/plugins/serverless_search/common/types/connector_stats.ts b/x-pack/plugins/serverless_search/common/types/connector_stats.ts
index b48d75120634d..39fe79bf8dad6 100644
--- a/x-pack/plugins/serverless_search/common/types/connector_stats.ts
+++ b/x-pack/plugins/serverless_search/common/types/connector_stats.ts
@@ -118,4 +118,5 @@ export interface SyncJobStatsByState {
idle: number;
running: number;
totalDurationSeconds: number;
+ topErrors: string[];
}
diff --git a/x-pack/plugins/serverless_search/server/collectors/connectors/telemetry.ts b/x-pack/plugins/serverless_search/server/collectors/connectors/telemetry.ts
index 7900265a4187c..0d03a79b9d2ee 100644
--- a/x-pack/plugins/serverless_search/server/collectors/connectors/telemetry.ts
+++ b/x-pack/plugins/serverless_search/server/collectors/connectors/telemetry.ts
@@ -130,6 +130,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -142,6 +146,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -154,6 +162,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -166,6 +178,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
last7Days: {
@@ -180,6 +196,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -192,6 +212,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -204,6 +228,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -216,6 +244,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
},
@@ -233,6 +265,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -245,6 +281,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -257,6 +297,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -269,6 +313,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
last7Days: {
@@ -283,6 +331,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
accessControl: {
total: { type: 'long' },
@@ -295,6 +347,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
full: {
total: { type: 'long' },
@@ -307,6 +363,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
incremental: {
total: { type: 'long' },
@@ -319,6 +379,10 @@ export const registerTelemetryUsageCollector = (
idle: { type: 'long' },
running: { type: 'long' },
totalDurationSeconds: { type: 'long' },
+ topErrors: {
+ type: 'array',
+ items: { type: 'keyword' },
+ },
},
},
},
diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
index 9143016851c50..7fbce312fde18 100644
--- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
+++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
@@ -8114,6 +8114,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8148,6 +8154,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8182,6 +8194,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8216,6 +8234,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -8254,6 +8278,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8288,6 +8318,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8322,6 +8358,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8356,6 +8398,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -8401,6 +8449,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8435,6 +8489,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8469,6 +8529,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8503,6 +8569,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -8541,6 +8613,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8575,6 +8653,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8609,6 +8693,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8643,6 +8733,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -8903,6 +8999,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8937,6 +9039,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -8971,6 +9079,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9005,6 +9119,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -9043,6 +9163,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9077,6 +9203,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9111,6 +9243,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9145,6 +9283,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -9190,6 +9334,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9224,6 +9374,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9258,6 +9414,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9292,6 +9454,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
@@ -9330,6 +9498,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9364,6 +9538,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9398,6 +9578,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
},
@@ -9432,6 +9618,12 @@
},
"totalDurationSeconds": {
"type": "long"
+ },
+ "topErrors": {
+ "type": "array",
+ "items": {
+ "type": "keyword"
+ }
}
}
}
diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json
index cc91e715030d4..c3a26e3258b4d 100644
--- a/x-pack/plugins/translations/translations/fr-FR.json
+++ b/x-pack/plugins/translations/translations/fr-FR.json
@@ -21119,6 +21119,17 @@
"xpack.infra.assetDetails.services.noServicesMsg": "Aucun service trouvé sur cet hôte. Cliquez sur {apmTutorialLink} pour instrumenter vos services avec APM.",
"xpack.infra.assetDetails.services.serviceButtonTitle": "{serviceName} signalé pour la dernière fois par {agentName}",
"xpack.infra.assetDetails.services.tooltip.servicesLabel": "Affichage {apmTutorialLink} des services détectés sur cet hôte.",
+ "xpack.infra.assetDetails.tabs.anomalies": "Anomalies",
+ "xpack.infra.assetDetails.tabs.apmLink": "APM",
+ "xpack.infra.assetDetails.tabs.linkToApm": "APM",
+ "xpack.infra.assetDetails.tabs.logs": "Logs",
+ "xpack.infra.assetDetails.tabs.metadata.seeLess": "Afficher moins",
+ "xpack.infra.assetDetails.tabs.metadata.seeMore": "+{count} de plus",
+ "xpack.infra.assetDetails.tabs.metadata": "Métadonnées",
+ "xpack.infra.assetDetails.tabs.osquery": "Osquery",
+ "xpack.infra.assetDetails.tabs.overview": "Aperçu",
+ "xpack.infra.assetDetails.tabs.processes": "Processus",
+ "xpack.infra.assetDetails.tabs.profiling": "Universal Profiling",
"xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "Dernières {duration} de données pour l'heure sélectionnée",
"xpack.infra.hostsViewPage.errorOnCreateOrLoadDataview": "Une erreur s'est produite lors de la création d'une vue de données : {metricAlias}. Essayez de recharger la page.",
"xpack.infra.hostsViewPage.kpi.subtitle.average.limit": "Moyenne (de {limit} hôtes)",
@@ -21195,7 +21206,6 @@
"xpack.infra.nodeContextMenu.viewLogsName": "Logs de {inventoryName}",
"xpack.infra.nodeContextMenu.viewMetricsName": "Indicateurs de {inventoryName}",
"xpack.infra.nodeContextMenu.viewUptimeLink": "{inventoryName} dans Uptime",
- "xpack.infra.nodeDetails.tabs.metadata.seeMore": "+{count} de plus",
"xpack.infra.parseInterval.errorMessage": "{value} n'est pas une chaîne d'intervalle",
"xpack.infra.profiling.flamegraphInfoPopoverBody": "Voir une représentation visuelle des fonctions qui consomment le plus de ressources. Chaque rectangle représente une fonction. La largeur du rectangle représente le temps passé sur la fonction et le nombre de rectangles empilés représente le nombre de fonctions appelées pour atteindre la fonction actuelle. {learnMoreLink}",
"xpack.infra.profiling.functionsInfoPopoverBody": "Identifiez les lignes de code les plus coûteuses sur votre hôte en examinant les fonctions les plus fréquemment échantillonnées, réparties par temps CPU, CO2 annualisé et estimations de coûts annualisés. {learnMoreLink}",
@@ -21276,7 +21286,6 @@
"xpack.infra.assetDetails.metadata.tooltip.metadata": "Métadonnées",
"xpack.infra.assetDetails.noActiveAlertsContentClosedSection": "Aucune alerte active",
"xpack.infra.assetDetails.overview.alertsSectionTitle": "Alertes",
- "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Aperçu Kubernetes",
"xpack.infra.assetDetails.overview.metadataSectionTitle": "Métadonnées",
"xpack.infra.assetDetails.overview.metricsSectionTitle": "Indicateurs",
"xpack.infra.assetDetails.overview.servicesSectionTitle": "Services",
@@ -21413,7 +21422,6 @@
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "Que sont ces indicateurs ?",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "Pourquoi des lignes pointillées apparaissent-elles ?",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "Créer une règle",
- "xpack.infra.infra.nodeDetails.apmTabLabel": "APM",
"xpack.infra.infra.nodeDetails.openAsPage": "Ouvrir en tant que page",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "Nom des données de reporting du groupe",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
@@ -21880,9 +21888,6 @@
"xpack.infra.metrics.nodeDetails.processListError": "Impossible de charger les données de processus",
"xpack.infra.metrics.nodeDetails.processListRetry": "Réessayer",
"xpack.infra.metrics.nodeDetails.searchForProcesses": "Rechercher les processus…",
- "xpack.infra.metrics.nodeDetails.tabs.metadata": "Métadonnées",
- "xpack.infra.metrics.nodeDetails.tabs.processes": "Processus",
- "xpack.infra.metrics.nodeDetails.tabs.profiling": "Universal Profiling",
"xpack.infra.metrics.pluginTitle": "Infrastructure",
"xpack.infra.metrics.refetchButtonLabel": "Rechercher de nouvelles données",
"xpack.infra.metrics.settingsTabTitle": "Paramètres",
@@ -22002,14 +22007,6 @@
"xpack.infra.nodeDetails.logs.openLogsLink": "Ouvrir dans Logs",
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "Rechercher les entrées de logs...",
"xpack.infra.nodeDetails.no": "Non",
- "xpack.infra.nodeDetails.tabs.anomalies": "Anomalies",
- "xpack.infra.nodeDetails.tabs.linkToApm": "APM",
- "xpack.infra.nodeDetails.tabs.logs": "Logs",
- "xpack.infra.nodeDetails.tabs.logs.title": "Logs",
- "xpack.infra.nodeDetails.tabs.metadata.seeLess": "Afficher moins",
- "xpack.infra.nodeDetails.tabs.metadata.title": "Métadonnées",
- "xpack.infra.nodeDetails.tabs.osquery": "Osquery",
- "xpack.infra.nodeDetails.tabs.overview.title": "Aperçu",
"xpack.infra.nodeDetails.yes": "Oui",
"xpack.infra.nodesToWaffleMap.groupsWithGroups.allName": "Tous",
"xpack.infra.nodesToWaffleMap.groupsWithNodes.allName": "Tous",
@@ -33024,8 +33021,6 @@
"xpack.securitySolution.flyout.entityDetails.observedEntityUpdatedTime": "Mis à jour le {time}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.title": "Entrée des risques : {description}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.titleDescription": "{quantity} sélectionnée",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextRange": "Affichage de {displayedRange} sur {totalContributions} {riskContributions}",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextSingle": "Affichage de {totalContributions} {riskInputs}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.text": "{totalSelectedContributions} contributions de risque sélectionnées",
"xpack.securitySolution.flyout.entityDetails.riskUpdatedTime": "Mis à jour le {time}",
"xpack.securitySolution.flyout.entityDetails.title": "Sommaire des risques de {entity} ",
@@ -34010,7 +34005,6 @@
"xpack.securitySolution.detectionEngine.alerts.createThresholdTimelineFailureTitle": "Impossible de créer une chronologie d'alerte de seuil",
"xpack.securitySolution.detectionEngine.alerts.documentTypeTitle": "Alertes",
"xpack.securitySolution.detectionEngine.alerts.fetchExceptionFilterFailure": "Erreur lors de la récupération du filtre d'exception.",
- "xpack.securitySolution.detectionEngine.alerts.histogram.allOthersGroupingLabel": "Toutes les autres",
"xpack.securitySolution.detectionEngine.alerts.histogram.headerTitle": "Tendance",
"xpack.securitySolution.detectionEngine.alerts.histogram.notAvailableTooltip": "Non disponible pour la vue de tendance",
"xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.groupByLabel": "Regrouper par",
@@ -36390,14 +36384,10 @@
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.ariaLabel": "Actions",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actionsColumn": "Actions",
"xpack.securitySolution.flyout.entityDetails.riskInputs.alertsTitle": "Alertes",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityDescription": "La criticité affectée au moment du calcul du score de risque.",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityTitle": "Criticité des ressources",
"xpack.securitySolution.flyout.entityDetails.riskInputs.dateColumn": "Date",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorBody": "Erreur lors de la récupération des entrées des risques. Réessayez plus tard.",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorTitle": "Un problème est survenu",
"xpack.securitySolution.flyout.entityDetails.riskInputs.riskInputColumn": "Contribution au risque",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInput": "Contribution au risque",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInputs": "Contributions au risque",
"xpack.securitySolution.flyout.entityDetails.riskSummary.casesAttachmentLabel": "Score de risque pour {entityType, select, host {host} user {user}} {entityName}",
"xpack.securitySolution.flyout.entityDetails.scoreColumnLabel": "Score",
"xpack.securitySolution.flyout.entityDetails.showAllRiskInputs": "Montrer toutes les entrées des risques",
@@ -36706,10 +36696,6 @@
"xpack.securitySolution.hosts.topRiskScoreContributors.ruleNameColumnTitle": "Nom de règle",
"xpack.securitySolution.hosts.topRiskScoreContributors.title": "Principaux contributeurs de score de risque",
"xpack.securitySolution.hosts.topRiskScoreContributorsTable.title": "Principaux contributeurs de score de risque",
- "xpack.securitySolution.hostsKpiHosts.errorSearchDescription": "Une erreur s'est produite lors de la recherche du KPI des hôtes",
- "xpack.securitySolution.hostsKpiHosts.failSearchDescription": "Impossible de lancer une recherche sur le KPI des hôtes",
- "xpack.securitySolution.hostsKpiUniqueIps.errorSearchDescription": "Une erreur s'est produite lors de la recherche des IP uniques du KPI des hôtes",
- "xpack.securitySolution.hostsKpiUniqueIps.failSearchDescription": "Impossible de lancer une recherche sur les IP uniques du KPI des hôtes",
"xpack.securitySolution.hostsRiskTable.hostNameTitle": "Nom d'hôte",
"xpack.securitySolution.hostsRiskTable.hostRiskScoreTitle": "Score de risque de l'hôte",
"xpack.securitySolution.hostsRiskTable.hostRiskTitle": "Risque de l'hôte",
@@ -36876,8 +36862,6 @@
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineIdFoundErrorMsg": "Aucun ID de chronologie n'a été trouvé",
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineNameFoundErrorMsg": "Aucun nom de chronologie n'a été trouvé",
"xpack.securitySolution.markdownEditor.plugins.timeline.timelineErrorTitle": "Erreur de chronologie",
- "xpack.securitySolution.matrixHistogram.errorSearchDescription": "Une erreur s'est produite sur la recherche de l'histogramme de matrice",
- "xpack.securitySolution.matrixHistogram.failSearchDescription": "Impossible de lancer une recherche sur l'histogramme de matrice",
"xpack.securitySolution.ml.score.anomalousEntityTitle": "Entité anormale",
"xpack.securitySolution.ml.score.anomalyJobTitle": "Tâche",
"xpack.securitySolution.ml.score.detectedTitle": "Détecté",
@@ -36989,16 +36973,6 @@
"xpack.securitySolution.networkHttpTable.column.requestsTitle": "Demandes",
"xpack.securitySolution.networkHttpTable.column.statusTitle": "Statut",
"xpack.securitySolution.networkHttpTable.title": "Requêtes HTTP",
- "xpack.securitySolution.networkKpiDns.errorSearchDescription": "Une erreur s'est produite sur la recherche de DNS de KPI réseau",
- "xpack.securitySolution.networkKpiDns.failSearchDescription": "Impossible de lancer la recherche sur le DNS de KPI réseau",
- "xpack.securitySolution.networkKpiNetworkEvents.errorSearchDescription": "Une erreur s'est produite sur la recherche d'événements réseau de KPI réseau",
- "xpack.securitySolution.networkKpiNetworkEvents.failSearchDescription": "Impossible de lancer la recherche sur les événements réseau de KPI réseau",
- "xpack.securitySolution.networkKpiTlsHandshakes.errorSearchDescription": "Une erreur s'est produite sur la recherche d'établissements de liaison TLS avec le KPI réseau",
- "xpack.securitySolution.networkKpiTlsHandshakes.failSearchDescription": "Impossible de lancer la recherche sur les établissements de liaison TLS avec le KPI réseau",
- "xpack.securitySolution.networkKpiUniqueFlows.errorSearchDescription": "Une erreur s'est produite sur la recherche de flux uniques de KPI réseau",
- "xpack.securitySolution.networkKpiUniqueFlows.failSearchDescription": "Impossible de lancer la recherche sur les flux uniques de KPI réseau",
- "xpack.securitySolution.networkKpiUniquePrivateIps.errorSearchDescription": "Une erreur s'est produite sur la recherche d'IP privées uniques de KPI réseau",
- "xpack.securitySolution.networkKpiUniquePrivateIps.failSearchDescription": "Impossible de lancer la recherche sur les IP privées uniques de KPI réseau",
"xpack.securitySolution.networkTls.errorSearchDescription": "Une erreur s'est produite sur la recherche de TLS réseau",
"xpack.securitySolution.networkTls.failSearchDescription": "Impossible de lancer la recherche sur le TLS réseau",
"xpack.securitySolution.networkTopCountries.failSearchDescription": "Impossible de lancer la recherche sur les premiers pays du réseau",
@@ -38004,8 +37978,6 @@
"xpack.securitySolution.users.navigation.riskTitle": "Risque de l'utilisateur",
"xpack.securitySolution.users.navigation.userScoreOverTimeTitle": "Score de risque de l'utilisateur sur la durée",
"xpack.securitySolution.users.pageTitle": "Utilisateurs",
- "xpack.securitySolution.usersKpiAuthentications.errorSearchDescription": "Une erreur s'est produite lors de la recherche d'authentifications du KPI des utilisateurs",
- "xpack.securitySolution.usersKpiAuthentications.failSearchDescription": "Impossible de lancer une recherche sur les authentifications du KPI des utilisateurs",
"xpack.securitySolution.usersRiskTable.userNameTitle": "Nom d'utilisateur",
"xpack.securitySolution.usersRiskTable.userRiskScoreTitle": "Score de risque de l'utilisateur",
"xpack.securitySolution.usersTable.domainTitle": "Domaine",
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index d0e0deea4c4e6..192f99c0b98cb 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -21170,7 +21170,6 @@
"xpack.infra.nodeContextMenu.viewLogsName": "{inventoryName} ログ",
"xpack.infra.nodeContextMenu.viewMetricsName": "{inventoryName} メトリック",
"xpack.infra.nodeContextMenu.viewUptimeLink": "Uptime 内の {inventoryName}",
- "xpack.infra.nodeDetails.tabs.metadata.seeMore": "他 {count} 件",
"xpack.infra.parseInterval.errorMessage": "{value}は間隔文字列ではありません",
"xpack.infra.profiling.flamegraphInfoPopoverBody": "最も多くリソースを消費する関数を視覚的に表示します。各長方形は関数を表します。長方形の幅は関数で経過した時間を表します。積み上げられた長方形の数は、現在の関数に達するまでに呼び出された関数の数を表します。{learnMoreLink}",
"xpack.infra.snapshot.missingSnapshotMetricError": "{nodeType}の{metric}の集約を使用できません。",
@@ -21250,7 +21249,6 @@
"xpack.infra.assetDetails.metadata.tooltip.metadata": "メタデータ",
"xpack.infra.assetDetails.noActiveAlertsContentClosedSection": "アクティブアラートはありません",
"xpack.infra.assetDetails.overview.alertsSectionTitle": "アラート",
- "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Kubernetes概要",
"xpack.infra.assetDetails.overview.metadataSectionTitle": "メタデータ",
"xpack.infra.assetDetails.overview.metricsSectionTitle": "メトリック",
"xpack.infra.assetDetails.overview.servicesSectionTitle": "サービス",
@@ -21264,6 +21262,17 @@
"xpack.infra.assetDetails.table.services.noServices.tutorialLink": "こちら",
"xpack.infra.assetDetails.table.services.tooltip.tutorialLink": "APMがインストルメンテーションされました",
"xpack.infra.assetDetails.table.tooltip.alertsLink": "アラート。",
+ "xpack.infra.assetDetails.tabs.anomalies": "異常",
+ "xpack.infra.assetDetails.tabs.apmLink": "APM",
+ "xpack.infra.assetDetails.tabs.linkToApm": "APM",
+ "xpack.infra.assetDetails.tabs.logs": "ログ",
+ "xpack.infra.assetDetails.tabs.metadata.seeLess": "簡易表示",
+ "xpack.infra.assetDetails.tabs.metadata.seeMore": "他 {count} 件",
+ "xpack.infra.assetDetails.tabs.metadata": "メタデータ",
+ "xpack.infra.assetDetails.tabs.osquery": "Osquery",
+ "xpack.infra.assetDetails.tabs.overview": "概要",
+ "xpack.infra.assetDetails.tabs.processes": "プロセス",
+ "xpack.infra.assetDetails.tabs.profiling": "ユニバーサルプロファイリング",
"xpack.infra.assetDetails.tooltip.activeAlertsExplanation": "アクティブアラート",
"xpack.infra.bottomDrawer.kubernetesDashboardsLink": "Kubernetesダッシュボード",
"xpack.infra.chartSection.missingMetricDataBody": "このチャートはデータが欠けています。",
@@ -21387,7 +21396,6 @@
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "これらのメトリックは何か。",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "点線が表示されている理由",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "ルールを作成",
- "xpack.infra.infra.nodeDetails.apmTabLabel": "APM",
"xpack.infra.infra.nodeDetails.openAsPage": "ページとして開く",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "データを報告するグループの名前",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
@@ -21854,9 +21862,6 @@
"xpack.infra.metrics.nodeDetails.processListError": "プロセスデータを読み込めません",
"xpack.infra.metrics.nodeDetails.processListRetry": "再試行",
"xpack.infra.metrics.nodeDetails.searchForProcesses": "プロセスを検索…",
- "xpack.infra.metrics.nodeDetails.tabs.metadata": "メタデータ",
- "xpack.infra.metrics.nodeDetails.tabs.processes": "プロセス",
- "xpack.infra.metrics.nodeDetails.tabs.profiling": "ユニバーサルプロファイリング",
"xpack.infra.metrics.pluginTitle": "インフラストラクチャー",
"xpack.infra.metrics.refetchButtonLabel": "新規データを確認",
"xpack.infra.metrics.settingsTabTitle": "設定",
@@ -21976,14 +21981,6 @@
"xpack.infra.nodeDetails.logs.openLogsLink": "ログで開く",
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "ログエントリーを検索...",
"xpack.infra.nodeDetails.no": "いいえ",
- "xpack.infra.nodeDetails.tabs.anomalies": "異常",
- "xpack.infra.nodeDetails.tabs.linkToApm": "APM",
- "xpack.infra.nodeDetails.tabs.logs": "ログ",
- "xpack.infra.nodeDetails.tabs.logs.title": "ログ",
- "xpack.infra.nodeDetails.tabs.metadata.seeLess": "簡易表示",
- "xpack.infra.nodeDetails.tabs.metadata.title": "メタデータ",
- "xpack.infra.nodeDetails.tabs.osquery": "Osquery",
- "xpack.infra.nodeDetails.tabs.overview.title": "概要",
"xpack.infra.nodeDetails.yes": "はい",
"xpack.infra.nodesToWaffleMap.groupsWithGroups.allName": "すべて",
"xpack.infra.nodesToWaffleMap.groupsWithNodes.allName": "すべて",
@@ -32991,8 +32988,6 @@
"xpack.securitySolution.flyout.entityDetails.observedEntityUpdatedTime": "更新日時{time}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.title": "リスクインプット:{description}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.titleDescription": "{quantity}選択済み",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextRange": "{displayedRange}/{totalContributions} {riskContributions}を表示しています",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextSingle": "{totalContributions} {riskInputs}を表示しています",
"xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.text": "{totalSelectedContributions}選択されたリスク寄与",
"xpack.securitySolution.flyout.entityDetails.riskUpdatedTime": "更新日時{time}",
"xpack.securitySolution.flyout.entityDetails.title": "{entity}リスク概要",
@@ -33979,7 +33974,6 @@
"xpack.securitySolution.detectionEngine.alerts.createThresholdTimelineFailureTitle": "しきい値アラートタイムラインを作成できませんでした",
"xpack.securitySolution.detectionEngine.alerts.documentTypeTitle": "アラート",
"xpack.securitySolution.detectionEngine.alerts.fetchExceptionFilterFailure": "例外フィルターの取得エラー。",
- "xpack.securitySolution.detectionEngine.alerts.histogram.allOthersGroupingLabel": "その他すべて",
"xpack.securitySolution.detectionEngine.alerts.histogram.headerTitle": "傾向",
"xpack.securitySolution.detectionEngine.alerts.histogram.notAvailableTooltip": "傾向ビューでは使用できません",
"xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.groupByLabel": "グループ分けの条件",
@@ -36359,14 +36353,10 @@
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.ariaLabel": "アクション",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actionsColumn": "アクション",
"xpack.securitySolution.flyout.entityDetails.riskInputs.alertsTitle": "アラート",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityDescription": "リスクスコア計算時に割り当てられた重要度。",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityTitle": "アセット重要度",
"xpack.securitySolution.flyout.entityDetails.riskInputs.dateColumn": "日付",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorBody": "リスク情報の取得中にエラーが発生しました。しばらくたってから再試行してください。",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorTitle": "問題が発生しました",
"xpack.securitySolution.flyout.entityDetails.riskInputs.riskInputColumn": "リスク寄与",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInput": "リスク寄与",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInputs": "リスク寄与",
"xpack.securitySolution.flyout.entityDetails.riskSummary.casesAttachmentLabel": "{entityType, select, host {ホスト} user {ユーザー}} {entityName}のリスクスコア",
"xpack.securitySolution.flyout.entityDetails.scoreColumnLabel": "スコア",
"xpack.securitySolution.flyout.entityDetails.showAllRiskInputs": "すべてのリスクインプットを表示",
@@ -36675,10 +36665,6 @@
"xpack.securitySolution.hosts.topRiskScoreContributors.ruleNameColumnTitle": "ルール名",
"xpack.securitySolution.hosts.topRiskScoreContributors.title": "上位のリスクスコアの要因",
"xpack.securitySolution.hosts.topRiskScoreContributorsTable.title": "上位のリスクスコアの要因",
- "xpack.securitySolution.hostsKpiHosts.errorSearchDescription": "ホストKPIホスト検索でエラーが発生しました",
- "xpack.securitySolution.hostsKpiHosts.failSearchDescription": "ホストKPIホストで検索を実行できませんでした",
- "xpack.securitySolution.hostsKpiUniqueIps.errorSearchDescription": "ホストKPI一意のIP検索でエラーが発生しました",
- "xpack.securitySolution.hostsKpiUniqueIps.failSearchDescription": "ホストKPI一意のIPで検索を実行できませんでした",
"xpack.securitySolution.hostsRiskTable.hostNameTitle": "ホスト名",
"xpack.securitySolution.hostsRiskTable.hostRiskScoreTitle": "ホストリスクスコア",
"xpack.securitySolution.hostsRiskTable.hostRiskTitle": "ホストリスク",
@@ -36845,8 +36831,6 @@
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineIdFoundErrorMsg": "タイムラインIDが見つかりません",
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineNameFoundErrorMsg": "タイムライン名が見つかりません",
"xpack.securitySolution.markdownEditor.plugins.timeline.timelineErrorTitle": "タイムラインエラー",
- "xpack.securitySolution.matrixHistogram.errorSearchDescription": "行列ヒストグラム検索でエラーが発生しました",
- "xpack.securitySolution.matrixHistogram.failSearchDescription": "行列ヒストグラムで検索を実行できませんでした",
"xpack.securitySolution.ml.score.anomalousEntityTitle": "異常エンティティ",
"xpack.securitySolution.ml.score.anomalyJobTitle": "ジョブ名",
"xpack.securitySolution.ml.score.detectedTitle": "検出",
@@ -36958,16 +36942,6 @@
"xpack.securitySolution.networkHttpTable.column.requestsTitle": "リクエスト",
"xpack.securitySolution.networkHttpTable.column.statusTitle": "ステータス",
"xpack.securitySolution.networkHttpTable.title": "HTTPリクエスト",
- "xpack.securitySolution.networkKpiDns.errorSearchDescription": "ネットワークKPI DNS検索でエラーが発生しました",
- "xpack.securitySolution.networkKpiDns.failSearchDescription": "ネットワークKPI DNSで検索を実行できませんでした",
- "xpack.securitySolution.networkKpiNetworkEvents.errorSearchDescription": "ネットワークKPIネットワークイベント検索でエラーが発生しました",
- "xpack.securitySolution.networkKpiNetworkEvents.failSearchDescription": "ネットワークKPIネットワークイベントで検索を実行できませんでした",
- "xpack.securitySolution.networkKpiTlsHandshakes.errorSearchDescription": "ネットワークKPI TLSハンドシェイク検索でエラーが発生しました",
- "xpack.securitySolution.networkKpiTlsHandshakes.failSearchDescription": "ネットワークKPI TLSハンドシェイクで検索を実行できませんでした",
- "xpack.securitySolution.networkKpiUniqueFlows.errorSearchDescription": "ネットワークKPI一意のフロー検索でエラーが発生しました",
- "xpack.securitySolution.networkKpiUniqueFlows.failSearchDescription": "ネットワークKPI一意のフローで検索を実行できませんでした",
- "xpack.securitySolution.networkKpiUniquePrivateIps.errorSearchDescription": "ネットワークKPI一意のプライベートIP検索でエラーが発生しました",
- "xpack.securitySolution.networkKpiUniquePrivateIps.failSearchDescription": "ネットワークKPI一意のプライベートIPで検索を実行できませんでした",
"xpack.securitySolution.networkTls.errorSearchDescription": "ネットワークTLS検索でエラーが発生しました",
"xpack.securitySolution.networkTls.failSearchDescription": "ネットワークTLSで検索を実行できませんでした",
"xpack.securitySolution.networkTopCountries.failSearchDescription": "ネットワーク上位の国で検索を実行できませんでした",
@@ -37973,8 +37947,6 @@
"xpack.securitySolution.users.navigation.riskTitle": "ユーザーリスク",
"xpack.securitySolution.users.navigation.userScoreOverTimeTitle": "経時的なユーザーリスクスコア",
"xpack.securitySolution.users.pageTitle": "ユーザー",
- "xpack.securitySolution.usersKpiAuthentications.errorSearchDescription": "ユーザーKPI認証検索でエラーが発生しました",
- "xpack.securitySolution.usersKpiAuthentications.failSearchDescription": "ユーザーKPI認証で検索を実行できませんでした",
"xpack.securitySolution.usersRiskTable.userNameTitle": "ユーザー名",
"xpack.securitySolution.usersRiskTable.userRiskScoreTitle": "ユーザーリスクスコア",
"xpack.securitySolution.usersTable.domainTitle": "ドメイン",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index eb5efe21adb79..126739fbf7aea 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -21125,6 +21125,17 @@
"xpack.infra.assetDetails.services.noServicesMsg": "在此主机上未找到任何服务。单击 {apmTutorialLink} 可借助 APM 检测您的服务。",
"xpack.infra.assetDetails.services.serviceButtonTitle": "{agentName} 上次报告的 {serviceName}",
"xpack.infra.assetDetails.services.tooltip.servicesLabel": "正在显示在此主机上检测到的 {apmTutorialLink} 服务。",
+ "xpack.infra.assetDetails.tabs.anomalies": "异常",
+ "xpack.infra.assetDetails.tabs.apmLink": "APM",
+ "xpack.infra.assetDetails.tabs.linkToApm": "APM",
+ "xpack.infra.assetDetails.tabs.logs": "日志",
+ "xpack.infra.assetDetails.tabs.metadata.seeLess": "显示更少",
+ "xpack.infra.assetDetails.tabs.metadata.seeMore": "另外 {count} 个",
+ "xpack.infra.assetDetails.tabs.metadata": "元数据",
+ "xpack.infra.assetDetails.tabs.osquery": "Osquery",
+ "xpack.infra.assetDetails.tabs.overview": "概览",
+ "xpack.infra.assetDetails.tabs.processes": "进程",
+ "xpack.infra.assetDetails.tabs.profiling": "Universal Profiling",
"xpack.infra.homePage.toolbar.showingLastOneMinuteDataText": "选定时间过去 {duration}的数据",
"xpack.infra.hostsViewPage.errorOnCreateOrLoadDataview": "尝试创建数据视图时出错:{metricAlias}。尝试重新加载该页面。",
"xpack.infra.hostsViewPage.kpi.subtitle.average.limit": "平均值(属于 {limit} 台主机)",
@@ -21201,7 +21212,6 @@
"xpack.infra.nodeContextMenu.viewLogsName": "{inventoryName} 日志",
"xpack.infra.nodeContextMenu.viewMetricsName": "{inventoryName} 指标",
"xpack.infra.nodeContextMenu.viewUptimeLink": "Uptime 中的 {inventoryName}",
- "xpack.infra.nodeDetails.tabs.metadata.seeMore": "另外 {count} 个",
"xpack.infra.parseInterval.errorMessage": "{value} 不是时间间隔字符串",
"xpack.infra.profiling.flamegraphInfoPopoverBody": "查看消耗最多资源的函数的视觉表示形式。每个矩形表示一个函数。矩形宽度表示函数所花费的时间,堆叠矩形数目表示被调用以访问当前函数的函数数目。{learnMoreLink}",
"xpack.infra.profiling.functionsInfoPopoverBody": "通过分析按 CPU 时间、年化 CO2 和年化成本估计划分的最频繁采样的函数,确定您主机上的耗用最多资源的代码行。{learnMoreLink}",
@@ -21282,7 +21292,6 @@
"xpack.infra.assetDetails.metadata.tooltip.metadata": "元数据",
"xpack.infra.assetDetails.noActiveAlertsContentClosedSection": "无活动告警",
"xpack.infra.assetDetails.overview.alertsSectionTitle": "告警",
- "xpack.infra.assetDetails.overview.kubernetesMetricsSectionTitle": "Kubernetes 概览",
"xpack.infra.assetDetails.overview.metadataSectionTitle": "元数据",
"xpack.infra.assetDetails.overview.metricsSectionTitle": "指标",
"xpack.infra.assetDetails.overview.servicesSectionTitle": "服务",
@@ -21419,7 +21428,6 @@
"xpack.infra.hostsViewPage.tooltip.whatAreTheseMetricsLink": "这些指标是什么?",
"xpack.infra.hostsViewPage.tooltip.whyAmISeeingDottedLines": "为什么我看到的是虚线?",
"xpack.infra.infra.assetDetails.alerts.createAlertLink": "创建规则",
- "xpack.infra.infra.nodeDetails.apmTabLabel": "APM",
"xpack.infra.infra.nodeDetails.openAsPage": "以页面形式打开",
"xpack.infra.inventory.alerting.groupActionVariableDescription": "报告数据的组名称",
"xpack.infra.inventoryId.host.ipCodeLabel": "host.ip",
@@ -21886,9 +21894,6 @@
"xpack.infra.metrics.nodeDetails.processListError": "无法加载进程数据",
"xpack.infra.metrics.nodeDetails.processListRetry": "重试",
"xpack.infra.metrics.nodeDetails.searchForProcesses": "搜索进程……",
- "xpack.infra.metrics.nodeDetails.tabs.metadata": "元数据",
- "xpack.infra.metrics.nodeDetails.tabs.processes": "进程",
- "xpack.infra.metrics.nodeDetails.tabs.profiling": "Universal Profiling",
"xpack.infra.metrics.pluginTitle": "基础设施",
"xpack.infra.metrics.refetchButtonLabel": "检查新数据",
"xpack.infra.metrics.settingsTabTitle": "设置",
@@ -22008,14 +22013,6 @@
"xpack.infra.nodeDetails.logs.openLogsLink": "在日志中打开",
"xpack.infra.nodeDetails.logs.textFieldPlaceholder": "搜索日志条目......",
"xpack.infra.nodeDetails.no": "否",
- "xpack.infra.nodeDetails.tabs.anomalies": "异常",
- "xpack.infra.nodeDetails.tabs.linkToApm": "APM",
- "xpack.infra.nodeDetails.tabs.logs": "日志",
- "xpack.infra.nodeDetails.tabs.logs.title": "日志",
- "xpack.infra.nodeDetails.tabs.metadata.seeLess": "显示更少",
- "xpack.infra.nodeDetails.tabs.metadata.title": "元数据",
- "xpack.infra.nodeDetails.tabs.osquery": "Osquery",
- "xpack.infra.nodeDetails.tabs.overview.title": "概览",
"xpack.infra.nodeDetails.yes": "是",
"xpack.infra.nodesToWaffleMap.groupsWithGroups.allName": "全部",
"xpack.infra.nodesToWaffleMap.groupsWithNodes.allName": "全部",
@@ -33035,8 +33032,6 @@
"xpack.securitySolution.flyout.entityDetails.observedEntityUpdatedTime": "已更新 {time}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.title": "风险输入:{description}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.titleDescription": "{quantity} 个已选定",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextRange": "正在显示 {displayedRange} 个(共 {totalContributions} 个){riskContributions}",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.selectionTextSingle": "正在显示 {totalContributions} 个{riskInputs}",
"xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.text": "{totalSelectedContributions} 个选定的风险贡献",
"xpack.securitySolution.flyout.entityDetails.riskUpdatedTime": "已更新 {time}",
"xpack.securitySolution.flyout.entityDetails.title": "{entity} 风险摘要",
@@ -34022,7 +34017,6 @@
"xpack.securitySolution.detectionEngine.alerts.createThresholdTimelineFailureTitle": "无法创建阈值告警时间线",
"xpack.securitySolution.detectionEngine.alerts.documentTypeTitle": "告警",
"xpack.securitySolution.detectionEngine.alerts.fetchExceptionFilterFailure": "提取例外筛选时出错。",
- "xpack.securitySolution.detectionEngine.alerts.histogram.allOthersGroupingLabel": "所有其他",
"xpack.securitySolution.detectionEngine.alerts.histogram.headerTitle": "趋势",
"xpack.securitySolution.detectionEngine.alerts.histogram.notAvailableTooltip": "不适用于趋势视图",
"xpack.securitySolution.detectionEngine.alerts.histogram.stackByOptions.groupByLabel": "分组依据",
@@ -36402,14 +36396,10 @@
"xpack.securitySolution.flyout.entityDetails.riskInputs.actions.ariaLabel": "操作",
"xpack.securitySolution.flyout.entityDetails.riskInputs.actionsColumn": "操作",
"xpack.securitySolution.flyout.entityDetails.riskInputs.alertsTitle": "告警",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityDescription": "在计算风险分数时分配的关键度。",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.assetCriticalityTitle": "资产关键度",
"xpack.securitySolution.flyout.entityDetails.riskInputs.dateColumn": "日期",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorBody": "提取风险输入时出错。请稍后重试。",
"xpack.securitySolution.flyout.entityDetails.riskInputs.errorTitle": "出问题了",
"xpack.securitySolution.flyout.entityDetails.riskInputs.riskInputColumn": "风险贡献",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInput": "风险贡献",
- "xpack.securitySolution.flyout.entityDetails.riskInputs.utilityBar.riskInputs": "风险贡献",
"xpack.securitySolution.flyout.entityDetails.riskSummary.casesAttachmentLabel": "{entityType, select, host {主机} user {用户}} {entityName} 的风险分数",
"xpack.securitySolution.flyout.entityDetails.scoreColumnLabel": "分数",
"xpack.securitySolution.flyout.entityDetails.showAllRiskInputs": "显示所有风险输入",
@@ -36718,10 +36708,6 @@
"xpack.securitySolution.hosts.topRiskScoreContributors.ruleNameColumnTitle": "规则名称",
"xpack.securitySolution.hosts.topRiskScoreContributors.title": "风险分数主要因素",
"xpack.securitySolution.hosts.topRiskScoreContributorsTable.title": "风险分数主要因素",
- "xpack.securitySolution.hostsKpiHosts.errorSearchDescription": "搜索主机 KPI 主机时发生错误",
- "xpack.securitySolution.hostsKpiHosts.failSearchDescription": "无法对主机 KPI 主机执行搜索",
- "xpack.securitySolution.hostsKpiUniqueIps.errorSearchDescription": "搜索主机 KPI 唯一 IP 时发生错误",
- "xpack.securitySolution.hostsKpiUniqueIps.failSearchDescription": "无法对主机 KPI 唯一 IP 执行搜索",
"xpack.securitySolution.hostsRiskTable.hostNameTitle": "主机名",
"xpack.securitySolution.hostsRiskTable.hostRiskScoreTitle": "主机风险分数",
"xpack.securitySolution.hostsRiskTable.hostRiskTitle": "主机风险",
@@ -36888,8 +36874,6 @@
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineIdFoundErrorMsg": "找不到时间线 ID",
"xpack.securitySolution.markdownEditor.plugins.timeline.noTimelineNameFoundErrorMsg": "找不到时间线名称",
"xpack.securitySolution.markdownEditor.plugins.timeline.timelineErrorTitle": "时间线错误",
- "xpack.securitySolution.matrixHistogram.errorSearchDescription": "搜索矩阵直方图时发生错误",
- "xpack.securitySolution.matrixHistogram.failSearchDescription": "无法对矩阵直方图执行搜索",
"xpack.securitySolution.ml.score.anomalousEntityTitle": "异常实体",
"xpack.securitySolution.ml.score.anomalyJobTitle": "作业",
"xpack.securitySolution.ml.score.detectedTitle": "已检测到",
@@ -37001,16 +36985,6 @@
"xpack.securitySolution.networkHttpTable.column.requestsTitle": "请求",
"xpack.securitySolution.networkHttpTable.column.statusTitle": "状态",
"xpack.securitySolution.networkHttpTable.title": "HTTP 请求",
- "xpack.securitySolution.networkKpiDns.errorSearchDescription": "搜索网络 KPI DNS 时发生错误",
- "xpack.securitySolution.networkKpiDns.failSearchDescription": "无法对网络 KPI DNS 执行搜索",
- "xpack.securitySolution.networkKpiNetworkEvents.errorSearchDescription": "搜索网络 KPI 网络事件时发生错误",
- "xpack.securitySolution.networkKpiNetworkEvents.failSearchDescription": "无法对网络 KPI 网络事件执行搜索",
- "xpack.securitySolution.networkKpiTlsHandshakes.errorSearchDescription": "搜索网络 KPI TLS 握手时发生错误",
- "xpack.securitySolution.networkKpiTlsHandshakes.failSearchDescription": "无法对网络 KPI TLS 握手执行搜索",
- "xpack.securitySolution.networkKpiUniqueFlows.errorSearchDescription": "搜索网络 KPI 唯一流时发生错误",
- "xpack.securitySolution.networkKpiUniqueFlows.failSearchDescription": "无法对网络 KPI 唯一流执行搜索",
- "xpack.securitySolution.networkKpiUniquePrivateIps.errorSearchDescription": "搜索网络 KPI 唯一专用 IP 时发生错误",
- "xpack.securitySolution.networkKpiUniquePrivateIps.failSearchDescription": "无法对网络 KPI 唯一专用 IP 执行搜索",
"xpack.securitySolution.networkTls.errorSearchDescription": "搜索网络 TLS 时发生错误",
"xpack.securitySolution.networkTls.failSearchDescription": "无法对网络 TLS 执行搜索",
"xpack.securitySolution.networkTopCountries.failSearchDescription": "无法对网络热门国家/地区执行搜索",
@@ -38016,8 +37990,6 @@
"xpack.securitySolution.users.navigation.riskTitle": "用户风险",
"xpack.securitySolution.users.navigation.userScoreOverTimeTitle": "一段时间的用户风险分数",
"xpack.securitySolution.users.pageTitle": "用户",
- "xpack.securitySolution.usersKpiAuthentications.errorSearchDescription": "搜索用户 KPI 身份验证时发生错误",
- "xpack.securitySolution.usersKpiAuthentications.failSearchDescription": "无法对用户 KPI 身份验证执行搜索",
"xpack.securitySolution.usersRiskTable.userNameTitle": "用户名",
"xpack.securitySolution.usersRiskTable.userRiskScoreTitle": "用户风险分数",
"xpack.securitySolution.usersTable.domainTitle": "域",
diff --git a/x-pack/test/api_integration/apis/security_solution/index.js b/x-pack/test/api_integration/apis/security_solution/index.js
index ba35ac7240852..501e2de697ec9 100644
--- a/x-pack/test/api_integration/apis/security_solution/index.js
+++ b/x-pack/test/api_integration/apis/security_solution/index.js
@@ -11,9 +11,6 @@ export default function ({ loadTestFile }) {
loadTestFile(require.resolve('./events'));
loadTestFile(require.resolve('./hosts'));
loadTestFile(require.resolve('./host_details'));
- loadTestFile(require.resolve('./kpi_network'));
- loadTestFile(require.resolve('./kpi_hosts'));
- loadTestFile(require.resolve('./matrix_dns_histogram'));
loadTestFile(require.resolve('./network_details'));
loadTestFile(require.resolve('./network_dns'));
loadTestFile(require.resolve('./network_top_n_flow'));
diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts
deleted file mode 100644
index d1ae827c535a3..0000000000000
--- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import expect from '@kbn/expect';
-import {
- HostsKpiHostsStrategyResponse,
- HostsKpiQueries,
- HostsKpiUniqueIpsStrategyResponse,
-} from '@kbn/security-solution-plugin/common/search_strategy';
-import { FtrProviderContext } from '../../ftr_provider_context';
-
-export default function ({ getService }: FtrProviderContext) {
- const esArchiver = getService('esArchiver');
- const supertest = getService('supertest');
- const bsearch = getService('bsearch');
-
- describe('Kpi Hosts', () => {
- describe('With filebeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/kpi_hosts')
- );
- after(
- async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/kpi_hosts')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- hosts: 1,
- hostsHistogram: [
- {
- x: new Date('2019-02-09T16:45:06.000Z').valueOf(),
- y: 1,
- },
- ],
- uniqueSourceIps: 1,
- uniqueSourceIpsHistogram: [
- {
- x: new Date('2019-02-09T16:45:06.000Z').valueOf(),
- y: 1,
- },
- ],
- uniqueDestinationIps: 1,
- uniqueDestinationIpsHistogram: [
- {
- x: new Date('2019-02-09T16:45:06.000Z').valueOf(),
- y: 1,
- },
- ],
- };
-
- it('Make sure that we get KpiHosts data', async () => {
- const kpiHosts = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: HostsKpiQueries.kpiHosts,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram);
- expect(kpiHosts.hosts).to.eql(expectedResult.hosts);
- });
-
- it('Make sure that we get KpiUniqueIps data', async () => {
- const body = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: HostsKpiQueries.kpiUniqueIps,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(body.uniqueDestinationIps).to.eql(expectedResult.uniqueDestinationIps);
- expect(body.uniqueDestinationIpsHistogram).to.eql(
- expectedResult.uniqueDestinationIpsHistogram
- );
- expect(body.uniqueSourceIps).to.eql(expectedResult.uniqueSourceIps);
- expect(body.uniqueSourceIpsHistogram).to.eql(expectedResult.uniqueSourceIpsHistogram);
- });
- });
-
- describe('With auditbeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')
- );
- after(
- async () =>
- await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/kpi_hosts')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- hosts: 3,
- hostsHistogram: [
- {
- x: new Date('2018-11-27T00:00:00.000Z').valueOf(),
- y: 1,
- },
- {
- x: new Date('2018-11-27T00:30:00.000Z').valueOf(),
- y: 0,
- },
- {
- x: new Date('2018-11-27T01:00:00.000Z').valueOf(),
- y: 0,
- },
- {
- x: new Date('2018-11-27T01:30:00.000Z').valueOf(),
- y: 0,
- },
- {
- x: new Date('2018-11-27T02:00:00.000Z').valueOf(),
- y: 1,
- },
- {
- x: new Date('2018-11-27T02:30:00.000Z').valueOf(),
- y: 1,
- },
- ],
- uniqueSourceIps: 3,
- uniqueSourceIpsHistogram: [
- { x: 1543276800000, y: 1 },
- { x: 1543278600000, y: 0 },
- { x: 1543280400000, y: 0 },
- { x: 1543282200000, y: 0 },
- { x: 1543284000000, y: 1 },
- { x: 1543285800000, y: 1 },
- ],
- uniqueDestinationIps: 0,
- uniqueDestinationIpsHistogram: [
- { x: 1543276800000, y: 0 },
- { x: 1543278600000, y: 0 },
- { x: 1543280400000, y: 0 },
- { x: 1543282200000, y: 0 },
- { x: 1543284000000, y: 0 },
- { x: 1543285800000, y: 0 },
- ],
- };
-
- it('Make sure that we get KpiHosts data', async () => {
- const kpiHosts = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: HostsKpiQueries.kpiHosts,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['auditbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiHosts.hostsHistogram).to.eql(expectedResult.hostsHistogram);
- expect(kpiHosts.hosts).to.eql(expectedResult.hosts);
- });
-
- it('Make sure that we get KpiUniqueIps data', async () => {
- const body = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: HostsKpiQueries.kpiUniqueIps,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['auditbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps);
- expect(body.uniqueDestinationIpsHistogram!).to.eql(
- expectedResult.uniqueDestinationIpsHistogram
- );
- expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps);
- expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram);
- });
- });
- });
-}
diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts
deleted file mode 100644
index 446797d993294..0000000000000
--- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import expect from '@kbn/expect';
-import {
- NetworkKpiDnsStrategyResponse,
- NetworkKpiNetworkEventsStrategyResponse,
- NetworkKpiQueries,
- NetworkKpiTlsHandshakesStrategyResponse,
- NetworkKpiUniqueFlowsStrategyResponse,
- NetworkKpiUniquePrivateIpsStrategyResponse,
-} from '@kbn/security-solution-plugin/common/search_strategy';
-import { FtrProviderContext } from '../../ftr_provider_context';
-
-export default function ({ getService }: FtrProviderContext) {
- const esArchiver = getService('esArchiver');
- const supertest = getService('supertest');
- const bsearch = getService('bsearch');
-
- describe('Kpi Network', () => {
- describe('With filebeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')
- );
- after(
- async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- networkEvents: 6157,
- uniqueFlowId: 712,
- uniqueSourcePrivateIps: 8,
- uniqueSourcePrivateIpsHistogram: [
- {
- x: new Date('2019-02-09T16:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-09T19:00:00.000Z').valueOf(),
- y: 0,
- },
- {
- x: new Date('2019-02-09T22:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-10T01:00:00.000Z').valueOf(),
- y: 7,
- },
- ],
- uniqueDestinationPrivateIps: 9,
- uniqueDestinationPrivateIpsHistogram: [
- {
- x: new Date('2019-02-09T16:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-09T19:00:00.000Z').valueOf(),
- y: 0,
- },
- {
- x: new Date('2019-02-09T22:00:00.000Z').valueOf(),
- y: 8,
- },
- {
- x: new Date('2019-02-10T01:00:00.000Z').valueOf(),
- y: 8,
- },
- ],
- dnsQueries: 169,
- tlsHandshakes: 62,
- };
-
- it('Make sure that we get KpiNetwork uniqueFlows data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.uniqueFlows,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId);
- });
-
- it('Make sure that we get KpiNetwork networkEvents data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.networkEvents,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents);
- });
-
- it('Make sure that we get KpiNetwork DNS data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.dns,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries);
- });
-
- it('Make sure that we get KpiNetwork networkEvents data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.networkEvents,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents);
- });
-
- it('Make sure that we get KpiNetwork tlsHandshakes data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.tlsHandshakes,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
-
- expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes);
- });
-
- it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.uniquePrivateIps,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
-
- expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql(
- expectedResult.uniqueDestinationPrivateIps
- );
- expect(kpiNetwork.uniqueDestinationPrivateIpsHistogram).to.eql(
- expectedResult.uniqueDestinationPrivateIpsHistogram
- );
- expect(kpiNetwork.uniqueSourcePrivateIps).to.eql(expectedResult.uniqueSourcePrivateIps);
- expect(kpiNetwork.uniqueSourcePrivateIpsHistogram).to.eql(
- expectedResult.uniqueSourcePrivateIpsHistogram
- );
- });
- });
-
- describe('With packetbeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/packetbeat/default')
- );
- after(
- async () => await esArchiver.unload('x-pack/test/functional/es_archives/packetbeat/default')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- networkEvents: 665,
- uniqueFlowId: 124,
- uniqueSourcePrivateIps: 0,
- uniqueSourcePrivateIpsHistogram: null,
- uniqueDestinationPrivateIps: 0,
- uniqueDestinationPrivateIpsHistogram: null,
- dnsQueries: 0,
- tlsHandshakes: 1,
- };
-
- it('Make sure that we get KpiNetwork uniqueFlows data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.uniqueFlows,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['packetbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId);
- });
-
- it('Make sure that we get KpiNetwork DNS data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.dns,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['packetbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries);
- });
-
- it('Make sure that we get KpiNetwork networkEvents data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.networkEvents,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['packetbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
-
- expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents);
- });
-
- it('Make sure that we get KpiNetwork tlsHandshakes data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.tlsHandshakes,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['packetbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes);
- });
-
- it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => {
- const kpiNetwork = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: NetworkKpiQueries.uniquePrivateIps,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['packetbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
-
- expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql(
- expectedResult.uniqueDestinationPrivateIps
- );
- expect(kpiNetwork.uniqueDestinationPrivateIpsHistogram).to.eql(
- expectedResult.uniqueDestinationPrivateIpsHistogram
- );
- expect(kpiNetwork.uniqueSourcePrivateIps).to.eql(expectedResult.uniqueSourcePrivateIps);
- expect(kpiNetwork.uniqueSourcePrivateIpsHistogram).to.eql(
- expectedResult.uniqueSourcePrivateIpsHistogram
- );
- });
- });
- });
-}
diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_users.ts b/x-pack/test/api_integration/apis/security_solution/kpi_users.ts
deleted file mode 100644
index 20861f8856c83..0000000000000
--- a/x-pack/test/api_integration/apis/security_solution/kpi_users.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import expect from '@kbn/expect';
-import {
- UsersKpiAuthenticationsStrategyResponse,
- UsersQueries,
-} from '@kbn/security-solution-plugin/common/search_strategy';
-import { FtrProviderContext } from '../../ftr_provider_context';
-
-export default function ({ getService }: FtrProviderContext) {
- const esArchiver = getService('esArchiver');
- const supertest = getService('supertest');
- const bsearch = getService('bsearch');
-
- describe('Kpi Users', () => {
- describe('With filebeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/filebeat/default')
- );
- after(
- async () => await esArchiver.unload('x-pack/test/functional/es_archives/filebeat/default')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- authSuccess: 0,
- authSuccessHistogram: null,
- authFailure: 0,
- authFailureHistogram: null,
- };
-
- it('Make sure that we get KpiAuthentications data', async () => {
- const body = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: UsersQueries.kpiAuthentications,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['filebeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess);
- expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram);
- expect(body.authenticationsFailure).to.eql(expectedResult.authFailure);
- expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram);
- });
- });
-
- describe('With auditbeat', () => {
- before(
- async () => await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/default')
- );
- after(
- async () => await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/default')
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
- const expectedResult = {
- authSuccess: 0,
- authSuccessHistogram: null,
- authFailure: 0,
- authFailureHistogram: null,
- };
-
- it('Make sure that we get KpiAuthentications data', async () => {
- const body = await bsearch.send({
- supertest,
- options: {
- factoryQueryType: UsersQueries.kpiAuthentications,
- timerange: {
- interval: '12h',
- to: TO,
- from: FROM,
- },
- defaultIndex: ['auditbeat-*'],
- inspect: false,
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- expect(body.authenticationsSuccess).to.eql(expectedResult.authSuccess);
- expect(body.authenticationsSuccessHistogram).to.eql(expectedResult.authSuccessHistogram);
- expect(body.authenticationsFailure).to.eql(expectedResult.authFailure);
- expect(body.authenticationsFailureHistogram).to.eql(expectedResult.authFailureHistogram);
- });
- });
- });
-}
diff --git a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts b/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts
deleted file mode 100644
index f418536d5d744..0000000000000
--- a/x-pack/test/api_integration/apis/security_solution/matrix_dns_histogram.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import expect from '@kbn/expect';
-
-import {
- MatrixHistogramQuery,
- MatrixHistogramType,
- NetworkDnsStrategyResponse,
-} from '@kbn/security-solution-plugin/common/search_strategy';
-
-import { FtrProviderContext } from '../../ftr_provider_context';
-
-export default function ({ getService }: FtrProviderContext) {
- const esArchiver = getService('esArchiver');
- const supertest = getService('supertest');
- const bsearch = getService('bsearch');
-
- describe('Matrix DNS Histogram', () => {
- describe('Large data set', () => {
- before(
- async () =>
- await esArchiver.load(
- 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query'
- )
- );
-
- after(
- async () =>
- await esArchiver.unload(
- 'x-pack/test/functional/es_archives/security_solution/matrix_dns_histogram/large_dns_query'
- )
- );
-
- const FROM = '2000-01-01T00:00:00.000Z';
- const TO = '3000-01-01T00:00:00.000Z';
-
- it('Make sure that we get dns data without getting bucket errors when querying large volume of data', async () => {
- const networkDns = await bsearch.send({
- supertest,
- options: {
- defaultIndex: ['large_volume_dns_data'],
- factoryQueryType: MatrixHistogramQuery,
- histogramType: MatrixHistogramType.dns,
- filterQuery:
- '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}',
- isPtrIncluded: false,
- timerange: {
- interval: '12',
- to: TO,
- from: FROM,
- },
- },
- strategy: 'securitySolutionSearchStrategy',
- });
- // This can have a odd unknown flake if we do anything more strict than this.
- const dnsCount = networkDns.rawResponse.aggregations?.dns_count as unknown as {
- value: number;
- };
- expect(dnsCount.value).to.be.above(0);
- });
- });
- });
-}
diff --git a/x-pack/test/functional/apps/infra/home_page.ts b/x-pack/test/functional/apps/infra/home_page.ts
index fc5b346dfa59f..df22e6ac8ccba 100644
--- a/x-pack/test/functional/apps/infra/home_page.ts
+++ b/x-pack/test/functional/apps/infra/home_page.ts
@@ -158,9 +158,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- it('should render 8 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(8);
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => {
+ const hosts = await pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric);
+ expect(hosts.length).to.equal(chartsCount);
+ });
});
it('should show alerts', async () => {
@@ -179,6 +186,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
+ describe('Metrics Tab', () => {
+ before(async () => {
+ await pageObjects.assetDetails.clickMetricsTab();
+ });
+
+ it('should show metrics content', async () => {
+ await pageObjects.assetDetails.metricsChartsContentExists();
+ });
+ });
+
describe('Logs Tab', () => {
before(async () => {
await pageObjects.assetDetails.clickLogsTab();
diff --git a/x-pack/test/functional/apps/infra/hosts_view.ts b/x-pack/test/functional/apps/infra/hosts_view.ts
index d204fee22e7f5..c62451c630714 100644
--- a/x-pack/test/functional/apps/infra/hosts_view.ts
+++ b/x-pack/test/functional/apps/infra/hosts_view.ts
@@ -239,9 +239,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- it('should render 8 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(8);
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => {
+ const hosts = await pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric);
+ expect(hosts.length).to.equal(chartsCount);
+ });
});
it('should show all section as collapsible', async () => {
@@ -305,6 +312,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
+ describe('Metrics Tab', () => {
+ before(async () => {
+ await pageObjects.assetDetails.clickMetricsTab();
+ });
+
+ it('should show metrics content', async () => {
+ await pageObjects.assetDetails.metricsChartsContentExists();
+ });
+ });
+
describe('Processes Tab', () => {
before(async () => {
await pageObjects.assetDetails.clickProcessesTab();
diff --git a/x-pack/test/functional/apps/infra/node_details.ts b/x-pack/test/functional/apps/infra/node_details.ts
index 00b2c5fee0a17..88a8dfe9b2aa2 100644
--- a/x-pack/test/functional/apps/infra/node_details.ts
+++ b/x-pack/test/functional/apps/infra/node_details.ts
@@ -116,29 +116,34 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.assetDetails.clickOverviewTab();
});
- [{ tab: 'metadata' }, { tab: 'processes' }, { tab: 'logs' }, { tab: 'anomalies' }].forEach(
- ({ tab }) => {
- it(`should keep the same date range across tabs: ${tab}`, async () => {
- const clickFuncs: Record void> = {
- metadata: pageObjects.assetDetails.clickMetadataTab,
- processes: pageObjects.assetDetails.clickProcessesTab,
- logs: pageObjects.assetDetails.clickLogsTab,
- anomalies: pageObjects.assetDetails.clickAnomaliesTab,
- };
-
- await clickFuncs[tab]();
-
- const datePickerValue = await pageObjects.timePicker.getTimeConfig();
- expect(await pageObjects.timePicker.timePickerExists()).to.be(true);
- expect(datePickerValue.start).to.equal(
- START_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT)
- );
- expect(datePickerValue.end).to.equal(
- END_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT)
- );
- });
- }
- );
+ [
+ { tab: 'metadata' },
+ { tab: 'processes' },
+ { tab: 'metrics' },
+ { tab: 'logs' },
+ { tab: 'anomalies' },
+ ].forEach(({ tab }) => {
+ it(`should keep the same date range across tabs: ${tab}`, async () => {
+ const clickFuncs: Record void> = {
+ metadata: pageObjects.assetDetails.clickMetadataTab,
+ processes: pageObjects.assetDetails.clickProcessesTab,
+ logs: pageObjects.assetDetails.clickLogsTab,
+ anomalies: pageObjects.assetDetails.clickAnomaliesTab,
+ metrics: pageObjects.assetDetails.clickMetricsTab,
+ };
+
+ await clickFuncs[tab]();
+
+ const datePickerValue = await pageObjects.timePicker.getTimeConfig();
+ expect(await pageObjects.timePicker.timePickerExists()).to.be(true);
+ expect(datePickerValue.start).to.equal(
+ START_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT)
+ );
+ expect(datePickerValue.end).to.equal(
+ END_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT)
+ );
+ });
+ });
it('preserves selected date range between page reloads', async () => {
const start = moment.utc(START_HOST_ALERTS_DATE).format(DATE_PICKER_FORMAT);
@@ -193,9 +198,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- it('should render 11 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(11);
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => {
+ const hosts = await pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric);
+ expect(hosts.length).to.equal(chartsCount);
+ });
});
it('should show all section as collapsable', async () => {
@@ -357,6 +369,29 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
+ describe('Metrics Tab', () => {
+ before(async () => {
+ await pageObjects.assetDetails.clickMetricsTab();
+ });
+
+ [
+ { metric: 'cpu', chartsCount: 4 },
+ { metric: 'memory', chartsCount: 2 },
+ { metric: 'disk', chartsCount: 3 },
+ { metric: 'network', chartsCount: 1 },
+ { metric: 'log', chartsCount: 1 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s)`, async () => {
+ const charts = await pageObjects.assetDetails.getMetricsTabHostCharts(metric);
+ expect(charts.length).to.equal(chartsCount);
+ });
+
+ it(`should render a quick access for ${metric} in the side panel`, async () => {
+ await pageObjects.assetDetails.quickAccessItemExists(metric);
+ });
+ });
+ });
+
describe('Processes Tab', () => {
before(async () => {
await pageObjects.assetDetails.clickProcessesTab();
@@ -405,7 +440,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
it('should render logs tab', async () => {
- await testSubjects.existOrFail('infraAssetDetailsLogsTabContent');
+ await pageObjects.assetDetails.logsExists();
});
it('preserves search term between page reloads', async () => {
@@ -457,7 +492,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('should render alerts count for a host inside a flyout', async () => {
await pageObjects.assetDetails.clickOverviewTab();
- retry.tryForTime(30 * 1000, async () => {
+ await retry.tryForTime(30 * 1000, async () => {
await observability.components.alertSummaryWidget.getFullSizeComponentSelectorOrFail();
});
@@ -511,14 +546,53 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
});
});
- it('should render 11 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(11);
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ { metric: 'kubernetes', chartsCount: 2 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart`, async () => {
+ await retry.try(async () => {
+ const charts = await (metric === 'kubernetes'
+ ? pageObjects.assetDetails.getOverviewTabKubernetesMetricCharts()
+ : pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric));
+
+ expect(charts.length).to.equal(chartsCount);
+ });
+ });
+ });
+ });
+
+ describe('Metrics Tab', () => {
+ before(async () => {
+ await pageObjects.assetDetails.clickMetricsTab();
});
- it('should render 4 charts in the Kubernetes Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsKubernetesMetricsCharts();
- expect(hosts.length).to.equal(4);
+ [
+ { metric: 'cpu', chartsCount: 4 },
+ { metric: 'memory', chartsCount: 2 },
+ { metric: 'disk', chartsCount: 3 },
+ { metric: 'network', chartsCount: 1 },
+ { metric: 'log', chartsCount: 1 },
+ { metric: 'kubernetes', chartsCount: 4 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s)`, async () => {
+ retry.try(async () => {
+ const charts = await (metric === 'kubernetes'
+ ? pageObjects.assetDetails.getMetricsTabKubernetesCharts()
+ : pageObjects.assetDetails.getMetricsTabHostCharts(metric));
+
+ expect(charts.length).to.equal(chartsCount);
+ });
+ });
+
+ it(`should render a quick access for ${metric} in the side panel`, async () => {
+ await retry.try(async () => {
+ await pageObjects.assetDetails.quickAccessItemExists(metric);
+ });
+ });
});
});
});
diff --git a/x-pack/test/functional/page_objects/asset_details.ts b/x-pack/test/functional/page_objects/asset_details.ts
index 9fb4fbc7cfe39..98dbf04b0d2b1 100644
--- a/x-pack/test/functional/page_objects/asset_details.ts
+++ b/x-pack/test/functional/page_objects/asset_details.ts
@@ -21,6 +21,10 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return testSubjects.click('infraAssetDetailsOpenAsPageButton');
},
+ async hostMetricsChartsGroupExists(metric: string) {
+ return testSubjects.existOrFail(`infraAssetDetailsHostChartsSection${metric}`);
+ },
+
// Overview
async clickOverviewTab() {
return testSubjects.click('infraAssetDetailsOverviewTab');
@@ -36,13 +40,6 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return testSubjects.existOrFail('infraAssetDetailsAlertsTitle');
},
- async getAssetDetailsMetricsCharts() {
- const container = await testSubjects.find('infraAssetDetailsHostMetricsChartGrid');
- return container.findAllByCssSelector(
- '[data-test-subj*="infraAssetDetailsHostMetricsChart"]'
- );
- },
-
async getAssetDetailsServicesWithIconsAndNames() {
await testSubjects.existOrFail('infraAssetDetailsServicesContainer');
const container = await testSubjects.find('infraAssetDetailsServicesContainer');
@@ -70,13 +67,6 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return servicesWithIconsAndNames;
},
- async getAssetDetailsKubernetesMetricsCharts() {
- const container = await testSubjects.find('infraAssetDetailsKubernetesMetricsChartGrid');
- return container.findAllByCssSelector(
- '[data-test-subj*="infraAssetDetailsKubernetesMetricsChart"]'
- );
- },
-
async clickOverviewLinkToAlerts() {
return testSubjects.click('infraAssetDetailsAlertsShowAllButton');
},
@@ -90,51 +80,65 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
},
async cpuProfilingPromptExists() {
- return await testSubjects.existOrFail('infraAssetDetailsCPUProfilingPrompt');
+ return testSubjects.existOrFail('infraAssetDetailsCPUProfilingPrompt');
},
async cpuProfilingPromptMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsCPUProfilingPrompt');
+ return testSubjects.missingOrFail('infraAssetDetailsCPUProfilingPrompt');
},
async profilingTabExists() {
- return await testSubjects.existOrFail('infraAssetDetailsProfilingTab');
+ return testSubjects.existOrFail('infraAssetDetailsProfilingTab');
},
async profilingTabMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsProfilingTab');
+ return testSubjects.missingOrFail('infraAssetDetailsProfilingTab');
+ },
+
+ async getOverviewTabHostMetricCharts(metric: string) {
+ const container = await testSubjects.find('infraAssetDetailsOverviewTabContent');
+ const section = await container.findByTestSubject(
+ `infraAssetDetailsHostChartsSection${metric}`
+ );
+ return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]');
+ },
+
+ async getOverviewTabKubernetesMetricCharts() {
+ const container = await testSubjects.find('infraAssetDetailsOverviewTabContent');
+ const section = await container.findByTestSubject(`infraAssetDetailsKubernetesChartsSection`);
+ return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]');
},
// Collapsable sections
async metadataSectionCollapsibleExist() {
- return await testSubjects.existOrFail('infraAssetDetailsMetadataCollapsible');
+ return testSubjects.existOrFail('infraAssetDetailsMetadataCollapsible');
},
async alertsSectionCollapsibleExist() {
- return await testSubjects.existOrFail('infraAssetDetailsAlertsCollapsible');
+ return testSubjects.existOrFail('infraAssetDetailsAlertsCollapsible');
},
async servicesSectionCollapsibleExist() {
- return await testSubjects.existOrFail('infraAssetDetailsServicesCollapsible');
+ return testSubjects.existOrFail('infraAssetDetailsServicesCollapsible');
},
async metricsSectionCollapsibleExist() {
- return await testSubjects.existOrFail('infraAssetDetailsMetricsCollapsible');
+ return testSubjects.existOrFail('infraAssetDetailsMetricsCollapsible');
},
async alertsSectionCollapsibleClick() {
- return await testSubjects.click('infraAssetDetailsAlertsCollapsible');
+ return testSubjects.click('infraAssetDetailsAlertsCollapsible');
},
async alertsSectionClosedContentExist() {
- return await testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
+ return testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
},
async alertsSectionClosedContentMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
+ return testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
},
async alertsSectionClosedContentNoAlertsExist() {
- return await testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
+ return testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
},
async alertsSectionClosedContentNoAlertsMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
+ return testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
},
// Metadata
@@ -163,7 +167,7 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
},
async metadataTableMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsMetadataTable');
+ return testSubjects.missingOrFail('infraAssetDetailsMetadataTable');
},
async metadataRemovePinExists() {
@@ -184,7 +188,34 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
},
async getMetadataSearchField() {
- return await testSubjects.find('infraAssetDetailsMetadataSearchBarInput');
+ return testSubjects.find('infraAssetDetailsMetadataSearchBarInput');
+ },
+
+ // Metrics
+ async clickMetricsTab() {
+ return testSubjects.click('infraAssetDetailsMetricsTab');
+ },
+
+ async metricsChartsContentExists() {
+ return testSubjects.click('infraAssetDetailsMetricChartsContent');
+ },
+
+ async getMetricsTabHostCharts(metric: string) {
+ const container = await testSubjects.find('infraAssetDetailsMetricsTabContent');
+ const section = await container.findByTestSubject(
+ `infraAssetDetailsHostChartsSection${metric}`
+ );
+ return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]');
+ },
+
+ async getMetricsTabKubernetesCharts() {
+ const container = await testSubjects.find('infraAssetDetailsMetricsTabContent');
+ const section = await container.findByTestSubject(`infraAssetDetailsKubernetesChartsSection`);
+ return section.findAllByCssSelector('[data-test-subj*="infraAssetDetailsMetricChart"]');
+ },
+
+ async quickAccessItemExists(metric: string) {
+ return testSubjects.click(`infraMetricsQuickAccessItem${metric}`);
},
// Processes
@@ -220,15 +251,15 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
},
async getProcessesSearchField() {
- return await testSubjects.find('infraAssetDetailsProcessesSearchBarInput');
+ return testSubjects.find('infraAssetDetailsProcessesSearchBarInput');
},
async processesSearchInputErrorMissing() {
- return await testSubjects.missingOrFail('infraAssetDetailsProcessesSearchInputError');
+ return testSubjects.missingOrFail('infraAssetDetailsProcessesSearchInputError');
},
async processesSearchInputErrorExists() {
- return await testSubjects.existOrFail('infraAssetDetailsProcessesSearchInputError');
+ return testSubjects.existOrFail('infraAssetDetailsProcessesSearchInputError');
},
// Logs
@@ -237,11 +268,11 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
},
async logsExists() {
- await testSubjects.existOrFail('infraAssetDetailsLogsTabContent');
+ return testSubjects.existOrFail('infraAssetDetailsLogsTabContent');
},
async getLogsSearchField() {
- return await testSubjects.find('infraAssetDetailsLogsTabFieldSearch');
+ return testSubjects.find('infraAssetDetailsLogsTabFieldSearch');
},
// Anomalies
diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts
index 39edf24a96317..dcac33b952e3e 100644
--- a/x-pack/test/functional/services/observability/alerts/common.ts
+++ b/x-pack/test/functional/services/observability/alerts/common.ts
@@ -159,9 +159,16 @@ export function ObservabilityAlertsCommonProvider({
};
// Flyout
+ const getReasonMessageLinkByIndex = async (index: number) => {
+ const reasonMessageLinks = await find.allByCssSelector(
+ '[data-test-subj="o11yGetRenderCellValueLink"]'
+ );
+ return reasonMessageLinks[index] || null;
+ };
+
const openAlertsFlyout = retryOnStale.wrap(async (index: number = 0) => {
- await openActionsMenuForRow(index);
- await testSubjects.click('viewAlertDetailsFlyout');
+ const reasonMessageLink = await getReasonMessageLinkByIndex(index);
+ await reasonMessageLink.click();
await retry.waitFor(
'flyout open',
async () => await testSubjects.exists(ALERTS_FLYOUT_SELECTOR, { timeout: 2500 })
diff --git a/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts b/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts
index 7f2b386c7c736..b95861bf42bcc 100644
--- a/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts
+++ b/x-pack/test/observability_functional/apps/observability/pages/alert_details_page.ts
@@ -13,7 +13,7 @@ export default ({ getService }: FtrProviderContext) => {
const observability = getService('observability');
const retry = getService('retry');
- describe('Observability Alert Details page - Feature flag', function () {
+ describe('Observability Alert Details page', function () {
this.tags('includeFirefox');
before(async () => {
@@ -27,29 +27,36 @@ export default ({ getService }: FtrProviderContext) => {
await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs');
});
- it('should show 404 page when the feature flag is disabled but the alert exists', async () => {
- await observability.alerts.common.navigateToAlertDetails(
- '4c87bd11-ff31-4a05-8a04-833e2da94858'
- );
+ it('should show error when the alert does not exist', async () => {
+ await observability.alerts.common.navigateToAlertDetails('deleted-alert-Id');
await retry.waitFor(
- 'The 404 - Not found page to be visible',
- async () => await testSubjects.exists('pageNotFound')
+ 'Error message to be visible',
+ async () => await testSubjects.exists('alertDetailsError')
);
});
- // This test is will be removed after removing the feature flag.
- // FLAKY for the same reason: https://github.com/elastic/kibana/issues/133799
- describe.skip('Alert Detail / Alert Flyout', () => {
+
+ describe('Alert components', () => {
before(async () => {
await observability.alerts.common.navigateToTimeWithData();
});
- it('should open the flyout instead of the alerts details page when clicking on "View alert details" from the... (3 dots) button when the feature flag is disabled', async () => {
- await observability.alerts.common.openAlertsFlyout();
- await observability.alerts.common.getAlertsFlyoutOrFail();
+
+ it('should show tabbed view', async () => {
+ await observability.alerts.common.navigateToAlertDetails(
+ '4c87bd11-ff31-4a05-8a04-833e2da94858'
+ );
+
+ await retry.waitFor(
+ 'Overview tab to be visible',
+ async () => await testSubjects.exists('overviewTab')
+ );
+
+ await retry.waitFor(
+ 'Metadata tab to be visible',
+ async () => await testSubjects.exists('metadataTab')
+ );
});
- /* TODO: Add more test cases regarding the feature flag for:
- - alert details URL from the Action variable
- - alert details button from the alert flyout.
- */
+
+ /* TODO: Add more test cases */
});
});
};
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/threat_match.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match.ts
index 678750342f4ee..5514f7848b461 100644
--- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match.ts
+++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/rule_execution_logic/trial_license_complete_tier/execution_logic/threat_match.ts
@@ -5,6 +5,7 @@
* 2.0.
*/
+import { v4 as uuidv4 } from 'uuid';
import { get, isEqual, omit } from 'lodash';
import expect from '@kbn/expect';
import {
@@ -38,7 +39,13 @@ import {
import { RuleExecutionStatusEnum } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring';
import { getMaxSignalsWarning as getMaxAlertsWarning } from '@kbn/security-solution-plugin/server/lib/detection_engine/rule_types/utils/utils';
import { ENABLE_ASSET_CRITICALITY_SETTING } from '@kbn/security-solution-plugin/common/constants';
-import { previewRule, getAlerts, getPreviewAlerts } from '../../../../utils';
+import {
+ previewRule,
+ getAlerts,
+ getPreviewAlerts,
+ dataGeneratorFactory,
+ getThreatMatchRuleForAlertTesting,
+} from '../../../../utils';
import {
deleteAllAlerts,
deleteAllRules,
@@ -155,10 +162,15 @@ export default ({ getService }: FtrProviderContext) => {
const audibeatHostsPath = dataPathBuilder.getPath('auditbeat/hosts');
const threatIntelPath = dataPathBuilder.getPath('filebeat/threat_intel');
+ const { indexListOfDocuments } = dataGeneratorFactory({
+ es,
+ index: 'ecs_compliant',
+ log,
+ });
+
/**
* Specific api integration tests for threat matching rule type
*/
- // FLAKY: https://github.com/elastic/kibana/issues/155304
describe('@ess @serverless Threat match type rules', () => {
before(async () => {
await esArchiver.load(audibeatHostsPath);
@@ -1670,5 +1682,78 @@ export default ({ getService }: FtrProviderContext) => {
expect(fullAlert?.['user.asset.criticality']).to.eql('extreme_impact');
});
});
+
+ // https://github.com/elastic/kibana/issues/174573
+ describe('timestamp override and fallback timestamp', () => {
+ const timestamp = '2020-10-28T05:45:00.000Z';
+
+ const eventDoc = (id: string) => ({
+ id,
+ '@timestamp': timestamp,
+ host: { name: 'host-a' },
+ });
+
+ const threatDoc = (id: string) => ({
+ id,
+ '@timestamp': timestamp,
+ host: { name: 'host-a' },
+ 'agent.type': 'threat',
+ });
+
+ const threatMatchRule = (id: string): ThreatMatchRuleCreateProps => ({
+ ...getThreatMatchRuleForAlertTesting(['ecs_compliant']),
+ query: `id:${id} and NOT agent.type:threat`,
+ threat_query: `id:${id} and agent.type:threat`,
+ name: 'ALert suppression IM test rule',
+ from: 'now-35m',
+ interval: '30m',
+ timestamp_override: 'event.ingested',
+ timestamp_override_fallback_disabled: false,
+ });
+
+ it('should create alerts using a timestamp override and timestamp fallback enabled on threats first code path execution', async () => {
+ const id = uuidv4();
+
+ await indexListOfDocuments([eventDoc(id), eventDoc(id), threatDoc(id)]);
+
+ const { previewId, logs } = await previewRule({
+ supertest,
+ rule: threatMatchRule(id),
+ timeframeEnd: new Date('2020-10-28T06:00:00.000Z'),
+ invocationCount: 1,
+ });
+
+ const previewAlerts = await getPreviewAlerts({
+ es,
+ previewId,
+ sort: ['host.name', ALERT_ORIGINAL_TIME],
+ });
+
+ expect(previewAlerts.length).to.eql(2);
+ expect(logs[0].errors).to.have.length(0);
+ });
+
+ it('should create alert using a timestamp override and timestamp fallback enabled on events first code path execution', async () => {
+ const id = uuidv4();
+
+ await indexListOfDocuments([eventDoc(id), threatDoc(id), threatDoc(id)]);
+
+ const { previewId, logs } = await previewRule({
+ supertest,
+ rule: threatMatchRule(id),
+ timeframeEnd: new Date('2020-10-28T06:00:00.000Z'),
+ invocationCount: 1,
+ });
+
+ const previewAlerts = await getPreviewAlerts({
+ es,
+ previewId,
+ sort: ['host.name', ALERT_ORIGINAL_TIME],
+ });
+
+ expect(previewAlerts.length).to.eql(1);
+ expect(logs[0].errors).to.have.length(0);
+ });
+ });
});
};
diff --git a/x-pack/test/security_solution_cypress/config.ts b/x-pack/test/security_solution_cypress/config.ts
index 364327bf83b06..c3897eb24546a 100644
--- a/x-pack/test/security_solution_cypress/config.ts
+++ b/x-pack/test/security_solution_cypress/config.ts
@@ -46,7 +46,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
'--xpack.ruleRegistry.unsafe.legacyMultiTenancy.enabled=true',
`--xpack.securitySolution.enableExperimental=${JSON.stringify([
'alertSuppressionForNewTermsRuleEnabled',
- 'chartEmbeddablesEnabled',
])}`,
// mock cloud to enable the guided onboarding tour in e2e tests
'--xpack.cloud.id=test',
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts
index ff932a44b0da4..f27502dcd2bad 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/discover_timeline_state_integration.cy.ts
@@ -69,12 +69,12 @@ const handleIntercepts = () => {
});
};
-describe(
+// Failing: See https://github.com/elastic/kibana/issues/180755
+describe.skip(
'Discover Timeline State Integration',
{
tags: ['@ess', '@skipInServerless'],
},
-
() => {
beforeEach(() => {
login();
diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/esql_assistant.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/esql_assistant.cy.ts
index af66b1672c3e4..fa24e3ba901b2 100644
--- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/esql_assistant.cy.ts
+++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/timelines/esql/esql_assistant.cy.ts
@@ -20,7 +20,8 @@ const ESQL_QUERY = 'from auditbeat-* | where ecs.version == "8.0.0"';
const KQL_QUERY = '_index : "auditbeat-*" and ecs.version : "8.0.0"';
const EQL_QUERY = 'process where process.name == "zsh"';
-describe(
+// Failing: See https://github.com/elastic/kibana/issues/180756
+describe.skip(
'Basic Assistant tests',
{
tags: ['@ess'],
diff --git a/x-pack/test/security_solution_cypress/cypress/screens/sourcerer.ts b/x-pack/test/security_solution_cypress/cypress/screens/sourcerer.ts
index 450d1aa484b5d..0968be32ca005 100644
--- a/x-pack/test/security_solution_cypress/cypress/screens/sourcerer.ts
+++ b/x-pack/test/security_solution_cypress/cypress/screens/sourcerer.ts
@@ -30,4 +30,4 @@ export const SOURCERER = {
};
export const HOSTS_STAT =
- '[data-test-subj="stat-hosts"] [data-test-subj="stat-title"] [data-test-subj="metric_value"]';
+ '[data-test-embeddable-id="hostsKpiHostsQuery-hosts-metric-embeddable"] [data-test-subj="metric_value"]';
diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/data.json b/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/data.json
index ab66422fb9175..96b9c111aa605 100644
--- a/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/data.json
+++ b/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/data.json
@@ -22,6 +22,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "New Rule Test",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
},
@@ -30,6 +31,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "New Rule Test",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -64,6 +66,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -99,6 +102,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -133,6 +137,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -167,6 +172,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -201,6 +207,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -238,6 +245,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Endpoint Security",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -273,6 +281,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -307,6 +316,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -341,6 +351,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -375,6 +386,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -409,6 +421,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -446,6 +459,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Endpoint Security",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -480,6 +494,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -516,6 +531,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "New Rule Test",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
},
@@ -524,6 +540,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "New Rule Test",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -559,6 +576,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -595,6 +613,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -630,6 +649,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -665,6 +685,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -700,6 +721,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -738,6 +760,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Endpoint Security",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -774,6 +797,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -809,6 +833,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -844,6 +869,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -879,6 +905,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -914,6 +941,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -952,6 +980,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Endpoint Security",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
@@ -987,6 +1016,7 @@
"index": ".internal.alerts-security.alerts-default-000001",
"description": "Alert from Rule: Rule 2",
"category": "category_1",
+ "contribution_score": 50,
"risk_score": 70,
"timestamp": "2023-08-14T09:08:18.664Z"
}
diff --git a/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/mappings.json b/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/mappings.json
index 950fc2b610f6e..33b460c379eab 100644
--- a/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/mappings.json
+++ b/x-pack/test/security_solution_cypress/es_archives/risk_scores_new_complete_data/mappings.json
@@ -64,6 +64,9 @@
},
"timestamp": {
"type": "date"
+ },
+ "contribution_score": {
+ "type": "float"
}
}
},
@@ -130,6 +133,9 @@
},
"timestamp": {
"type": "date"
+ },
+ "contribution_score": {
+ "type": "float"
}
}
},
@@ -222,6 +228,9 @@
},
"timestamp": {
"type": "date"
+ },
+ "contribution_score": {
+ "type": "float"
}
}
},
@@ -279,6 +288,9 @@
},
"timestamp": {
"type": "date"
+ },
+ "contribution_score": {
+ "type": "float"
}
}
},
diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/hosts_page.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/hosts_page.ts
index 3b90fab7ef669..8bf1fe411d727 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/infra/hosts_page.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/infra/hosts_page.ts
@@ -85,9 +85,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.assetDetails.clickOverviewTab();
});
- it('should render 8 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(8);
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s) in the Metrics section`, async () => {
+ const charts = await pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric);
+ expect(charts.length).to.equal(chartsCount);
+ });
});
it('should show alerts', async () => {
@@ -96,7 +103,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const CreateRuleButtonExist = await testSubjects.exists(
'infraAssetDetailsCreateAlertsRuleButton'
);
- expect(CreateRuleButtonExist).to.be(false);
+ expect(CreateRuleButtonExist).to.be(true);
});
});
@@ -118,15 +125,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
it('should show processes title', async () => {
await await testSubjects.existOrFail('infraAssetDetailsTopProcessesTitle');
});
+ });
- describe('Logs Tab', () => {
- before(async () => {
- await pageObjects.assetDetails.clickLogsTab();
- });
+ describe('Logs Tab', () => {
+ before(async () => {
+ await pageObjects.assetDetails.clickLogsTab();
+ });
- it('should render logs tab', async () => {
- await pageObjects.assetDetails.logsExists();
- });
+ it('should render logs tab', async () => {
+ await pageObjects.assetDetails.logsExists();
});
});
});
diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/node_details.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/node_details.ts
index dd73fbf799b2e..4decf638741f0 100644
--- a/x-pack/test_serverless/functional/test_suites/observability/infra/node_details.ts
+++ b/x-pack/test_serverless/functional/test_suites/observability/infra/node_details.ts
@@ -20,6 +20,7 @@ const END_HOST_KUBERNETES_SECTION_DATE = moment.utc(
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
+ const retry = getService('retry');
const pageObjects = getPageObjects([
'assetDetails',
'common',
@@ -70,17 +71,25 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const CreateRuleButtonExist = await testSubjects.exists(
'infraAssetDetailsCreateAlertsRuleButton'
);
- expect(CreateRuleButtonExist).to.be(false);
+ expect(CreateRuleButtonExist).to.be(true);
});
- it('should render 11 charts in the Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsMetricsCharts();
- expect(hosts.length).to.equal(11);
- });
+ [
+ { metric: 'cpu', chartsCount: 2 },
+ { metric: 'memory', chartsCount: 1 },
+ { metric: 'disk', chartsCount: 2 },
+ { metric: 'network', chartsCount: 1 },
+ { metric: 'kubernetes', chartsCount: 2 },
+ ].forEach(({ metric, chartsCount }) => {
+ it(`should render ${chartsCount} ${metric} chart(s)`, async () => {
+ await retry.try(async () => {
+ const charts = await (metric === 'kubernetes'
+ ? pageObjects.assetDetails.getOverviewTabKubernetesMetricCharts()
+ : pageObjects.assetDetails.getOverviewTabHostMetricCharts(metric));
- it('should render 4 charts in the Kubernetes Metrics section', async () => {
- const hosts = await pageObjects.assetDetails.getAssetDetailsKubernetesMetricsCharts();
- expect(hosts.length).to.equal(4);
+ expect(charts.length).to.equal(chartsCount);
+ });
+ });
});
});
});