mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution] Rule Preview Table Follow-up (#128981)
This commit is contained in:
parent
4ff0a6e0af
commit
268470a440
26 changed files with 427 additions and 179 deletions
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
export enum RULE_PREVIEW_INVOCATION_COUNT {
|
||||
HOUR = 20,
|
||||
HOUR = 12,
|
||||
DAY = 24,
|
||||
WEEK = 168,
|
||||
MONTH = 30,
|
||||
|
|
|
@ -326,6 +326,7 @@ export enum TimelineId {
|
|||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
alternateTest = 'alternateTest',
|
||||
rulePreview = 'rule-preview',
|
||||
}
|
||||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
|
@ -339,6 +340,7 @@ export const TimelineIdLiteralRt = runtimeTypes.union([
|
|||
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.active),
|
||||
runtimeTypes.literal(TimelineId.test),
|
||||
runtimeTypes.literal(TimelineId.rulePreview),
|
||||
]);
|
||||
|
||||
export type TimelineIdLiteral = runtimeTypes.TypeOf<typeof TimelineIdLiteralRt>;
|
||||
|
|
|
@ -327,9 +327,9 @@ export const fillDefineEqlRuleAndContinue = (rule: CustomRule) => {
|
|||
cy.get(PREVIEW_HISTOGRAM)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
if (text !== 'Hits') {
|
||||
if (text !== 'Rule Preview') {
|
||||
cy.get(RULES_CREATION_PREVIEW).find(QUERY_PREVIEW_BUTTON).click({ force: true });
|
||||
cy.get(PREVIEW_HISTOGRAM).should('contain.text', 'Hits');
|
||||
cy.get(PREVIEW_HISTOGRAM).should('contain.text', 'Rule Preview');
|
||||
}
|
||||
});
|
||||
cy.get(TOAST_ERROR).should('not.exist');
|
||||
|
|
|
@ -82,6 +82,15 @@ describe('AlertSummaryView', () => {
|
|||
expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0);
|
||||
});
|
||||
|
||||
test('it does NOT render the action cell when readOnly is passed', () => {
|
||||
const { queryAllByTestId } = render(
|
||||
<TestProviders>
|
||||
<AlertSummaryView {...{ ...props, isReadOnly: true }} />
|
||||
</TestProviders>
|
||||
);
|
||||
expect(queryAllByTestId('hover-actions-filter-for').length).toEqual(0);
|
||||
});
|
||||
|
||||
test("render no investigation guide if it doesn't exist", async () => {
|
||||
(useRuleWithFallback as jest.Mock).mockReturnValue({
|
||||
rule: {
|
||||
|
|
|
@ -136,5 +136,18 @@ describe('getColumns', () => {
|
|||
).toEqual('hover-actions-copy-button');
|
||||
});
|
||||
});
|
||||
|
||||
describe('does not render hover actions when readOnly prop is passed', () => {
|
||||
test('it renders a filter for (+) button', () => {
|
||||
actionsColumn = getColumns({ ...defaultProps, isReadOnly: true })[0] as Column;
|
||||
const wrapper = mount(
|
||||
<TestProviders>{actionsColumn.render(testValue, testData)}</TestProviders>
|
||||
) as ReactWrapper;
|
||||
|
||||
expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="more-actions-agent.id"]').exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -140,5 +140,20 @@ describe('EventDetails', () => {
|
|||
alertsWrapper.find('[data-test-subj="threatIntelTab"]').first().simulate('click');
|
||||
expect(alertsWrapper.find('[data-test-subj="no-enrichments-found"]').exists()).toEqual(true);
|
||||
});
|
||||
it('does not render if readOnly prop is passed', async () => {
|
||||
const newProps = { ...defaultProps, isReadOnly: true };
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<EventDetails {...newProps} />
|
||||
</TestProviders>
|
||||
) as ReactWrapper;
|
||||
alertsWrapper = mount(
|
||||
<TestProviders>
|
||||
<EventDetails {...{ ...alertsProps, ...newProps }} />
|
||||
</TestProviders>
|
||||
) as ReactWrapper;
|
||||
await waitFor(() => wrapper.update());
|
||||
expect(alertsWrapper.find('[data-test-subj="threatIntelTab"]').exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -132,6 +132,25 @@ describe('EventFieldsBrowser', () => {
|
|||
expect(wrapper.find('[data-test-subj="more-actions-@timestamp"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it does not render hover actions when readOnly prop is passed', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<EventFieldsBrowser
|
||||
browserFields={mockBrowserFields}
|
||||
data={mockDetailItemData}
|
||||
eventId={eventId}
|
||||
timelineId="test"
|
||||
timelineTabType={TimelineTabs.query}
|
||||
isReadOnly
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="hover-actions-filter-for"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="hover-actions-filter-out"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="more-actions-@timestamp"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('it renders a column toggle button', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
|
|
|
@ -29,6 +29,20 @@ describe('Event Details Overview Cards', () => {
|
|||
getByText('Rule');
|
||||
});
|
||||
|
||||
it('renders only readOnly cards', () => {
|
||||
const { getByText, queryByText } = render(
|
||||
<TestProviders>
|
||||
<Overview {...propsWithReadOnly} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
getByText('Severity');
|
||||
getByText('Risk Score');
|
||||
|
||||
expect(queryByText('Status')).not.toBeInTheDocument();
|
||||
expect(queryByText('Rule')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders all cards it has data for', () => {
|
||||
const { getByText, queryByText } = render(
|
||||
<TestProviders>
|
||||
|
@ -194,3 +208,8 @@ const propsWithoutSeverity = {
|
|||
browserFields: { kibana: { fields: fieldsWithoutSeverity } },
|
||||
data: dataWithoutSeverity,
|
||||
};
|
||||
|
||||
const propsWithReadOnly = {
|
||||
...props,
|
||||
isReadOnly: true,
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useRouteSpy } from '../route/use_route_spy';
|
||||
|
||||
const hideTimelineForRoutes = [`/cases/configure`, '/administration'];
|
||||
const hideTimelineForRoutes = [`/cases/configure`, '/administration', 'rules/create'];
|
||||
|
||||
export const useShowTimeline = () => {
|
||||
const [{ pageName, pathName }] = useRouteSpy();
|
||||
|
|
|
@ -16,7 +16,10 @@ import { RowRendererId } from '../../../../common/types/timeline';
|
|||
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import { columns } from '../../configurations/security_solution_detections/columns';
|
||||
import {
|
||||
columns,
|
||||
rulePreviewColumns,
|
||||
} from '../../configurations/security_solution_detections/columns';
|
||||
|
||||
export const buildAlertStatusFilter = (status: Status): Filter[] => {
|
||||
const combinedQuery =
|
||||
|
@ -156,6 +159,19 @@ export const alertsDefaultModel: SubsetTimelineModel = {
|
|||
excludedRowRendererIds: Object.values(RowRendererId),
|
||||
};
|
||||
|
||||
export const alertsPreviewDefaultModel: SubsetTimelineModel = {
|
||||
...alertsDefaultModel,
|
||||
columns: rulePreviewColumns,
|
||||
defaultColumns: rulePreviewColumns,
|
||||
sort: [
|
||||
{
|
||||
columnId: 'kibana.alert.original_time',
|
||||
columnType: 'number',
|
||||
sortDirection: 'desc',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const requiredFieldsForActions = [
|
||||
'@timestamp',
|
||||
'kibana.alert.workflow_status',
|
||||
|
|
|
@ -234,7 +234,7 @@ export const getIsRulePreviewDisabled = ({
|
|||
if (ruleType === 'machine_learning') {
|
||||
return machineLearningJobId.length === 0;
|
||||
}
|
||||
if (ruleType === 'eql') {
|
||||
if (ruleType === 'eql' || ruleType === 'query' || ruleType === 'threshold') {
|
||||
return queryBar.query.query.length === 0;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -29,6 +29,13 @@ import { LoadingHistogram } from './loading_histogram';
|
|||
import { FieldValueThreshold } from '../threshold_input';
|
||||
import { isJobStarted } from '../../../../../common/machine_learning/helpers';
|
||||
|
||||
const HelpTextComponent = (
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem>{i18n.QUERY_PREVIEW_HELP_TEXT}</EuiFlexItem>
|
||||
<EuiFlexItem>{i18n.QUERY_PREVIEW_DISCLAIMER}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
||||
export interface RulePreviewProps {
|
||||
index: string[];
|
||||
isDisabled: boolean;
|
||||
|
@ -116,7 +123,7 @@ const RulePreviewComponent: React.FC<RulePreviewProps> = ({
|
|||
<>
|
||||
<EuiFormRow
|
||||
label={i18n.QUERY_PREVIEW_LABEL}
|
||||
helpText={i18n.QUERY_PREVIEW_HELP_TEXT}
|
||||
helpText={HelpTextComponent}
|
||||
error={undefined}
|
||||
isInvalid={false}
|
||||
data-test-subj="rule-preview"
|
||||
|
@ -156,7 +163,6 @@ const RulePreviewComponent: React.FC<RulePreviewProps> = ({
|
|||
previewId={previewId}
|
||||
addNoiseWarning={addNoiseWarning}
|
||||
spaceId={spaceId}
|
||||
threshold={threshold}
|
||||
index={index}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import * as i18n from '../rule_preview/translations';
|
||||
import { useGlobalTime } from '../../../../common/containers/use_global_time';
|
||||
|
@ -14,10 +14,12 @@ import { TestProviders } from '../../../../common/mock';
|
|||
import { usePreviewHistogram } from './use_preview_histogram';
|
||||
|
||||
import { PreviewHistogram } from './preview_histogram';
|
||||
import { ALL_VALUES_ZEROS_TITLE } from '../../../../common/components/charts/translation';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../../../../common/containers/use_global_time');
|
||||
jest.mock('./use_preview_histogram');
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../../../../common/components/url_state/normalize_time_range.ts');
|
||||
|
||||
describe('PreviewHistogram', () => {
|
||||
const mockSetQuery = jest.fn();
|
||||
|
@ -35,6 +37,37 @@ describe('PreviewHistogram', () => {
|
|||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('when there is no data', () => {
|
||||
(usePreviewHistogram as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
inspect: { dsl: [], response: [] },
|
||||
totalCount: 1,
|
||||
refetch: jest.fn(),
|
||||
data: [],
|
||||
buckets: [],
|
||||
},
|
||||
]);
|
||||
|
||||
test('it renders an empty histogram and table', () => {
|
||||
const wrapper = render(
|
||||
<TestProviders>
|
||||
<PreviewHistogram
|
||||
addNoiseWarning={jest.fn()}
|
||||
timeFrame="M"
|
||||
previewId={'test-preview-id'}
|
||||
spaceId={'default'}
|
||||
ruleType={'query'}
|
||||
index={['']}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.findByText('hello grid')).toBeTruthy();
|
||||
expect(wrapper.findByText(ALL_VALUES_ZEROS_TITLE)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test('it renders loader when isLoading is true', () => {
|
||||
(usePreviewHistogram as jest.Mock).mockReturnValue([
|
||||
true,
|
||||
|
@ -47,7 +80,7 @@ describe('PreviewHistogram', () => {
|
|||
},
|
||||
]);
|
||||
|
||||
const wrapper = mount(
|
||||
const wrapper = render(
|
||||
<TestProviders>
|
||||
<PreviewHistogram
|
||||
addNoiseWarning={jest.fn()}
|
||||
|
@ -60,72 +93,7 @@ describe('PreviewHistogram', () => {
|
|||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="preview-histogram-loading"]').exists()).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="header-section-subtitle"]').text()).toEqual(
|
||||
i18n.QUERY_PREVIEW_SUBTITLE_LOADING
|
||||
);
|
||||
});
|
||||
|
||||
test('it configures data and subtitle', () => {
|
||||
(usePreviewHistogram as jest.Mock).mockReturnValue([
|
||||
false,
|
||||
{
|
||||
inspect: { dsl: [], response: [] },
|
||||
totalCount: 9154,
|
||||
refetch: jest.fn(),
|
||||
data: [
|
||||
{ x: 1602247050000, y: 2314, g: 'All others' },
|
||||
{ x: 1602247162500, y: 3471, g: 'All others' },
|
||||
{ x: 1602247275000, y: 3369, g: 'All others' },
|
||||
],
|
||||
buckets: [],
|
||||
},
|
||||
]);
|
||||
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<PreviewHistogram
|
||||
addNoiseWarning={jest.fn()}
|
||||
timeFrame="M"
|
||||
previewId={'test-preview-id'}
|
||||
spaceId={'default'}
|
||||
ruleType={'query'}
|
||||
index={['']}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="preview-histogram-loading"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="header-section-subtitle"]').text()).toEqual(
|
||||
i18n.QUERY_PREVIEW_TITLE(9154)
|
||||
);
|
||||
expect(
|
||||
(
|
||||
wrapper.find('[data-test-subj="preview-histogram-bar-chart"]').props() as {
|
||||
barChart: unknown;
|
||||
}
|
||||
).barChart
|
||||
).toEqual([
|
||||
{
|
||||
key: 'hits',
|
||||
value: [
|
||||
{
|
||||
g: 'All others',
|
||||
x: 1602247050000,
|
||||
y: 2314,
|
||||
},
|
||||
{
|
||||
g: 'All others',
|
||||
x: 1602247162500,
|
||||
y: 3471,
|
||||
},
|
||||
{
|
||||
g: 'All others',
|
||||
x: 1602247275000,
|
||||
y: 3369,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(wrapper.findByTestId('preview-histogram-loading')).toBeTruthy();
|
||||
expect(wrapper.findByText(i18n.QUERY_PREVIEW_SUBTITLE_LOADING)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,19 +11,20 @@ import { Unit } from '@kbn/datemath';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer, EuiLoadingChart } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { Type } from '@kbn/securitysolution-io-ts-alerting-types';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { eventsViewerSelector } from '../../../../common/components/events_viewer/selectors';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features';
|
||||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import * as i18n from './translations';
|
||||
import { useGlobalTime } from '../../../../common/containers/use_global_time';
|
||||
import { getHistogramConfig, getThresholdHistogramConfig, isNoisy } from './helpers';
|
||||
import { getHistogramConfig, isNoisy } from './helpers';
|
||||
import { ChartSeriesConfigs, ChartSeriesData } from '../../../../common/components/charts/common';
|
||||
import { Panel } from '../../../../common/components/panel';
|
||||
import { HeaderSection } from '../../../../common/components/header_section';
|
||||
import { BarChart } from '../../../../common/components/charts/barchart';
|
||||
import { usePreviewHistogram } from './use_preview_histogram';
|
||||
import { formatDate } from '../../../../common/components/super_date_picker';
|
||||
import { FieldValueThreshold } from '../threshold_input';
|
||||
import { alertsDefaultModel } from '../../alerts_table/default_config';
|
||||
import { alertsPreviewDefaultModel } from '../../alerts_table/default_config';
|
||||
import { SourcererScopeName } from '../../../../common/store/sourcerer/model';
|
||||
import { defaultRowRenderers } from '../../../../timelines/components/timeline/body/renderers';
|
||||
import { TimelineId } from '../../../../../common/types';
|
||||
|
@ -35,6 +36,8 @@ import { PreviewRenderCellValue } from './preview_table_cell_renderer';
|
|||
import { getPreviewTableControlColumn } from './preview_table_control_columns';
|
||||
import { useGlobalFullScreen } from '../../../../common/containers/use_full_screen';
|
||||
import { InspectButtonContainer } from '../../../../common/components/inspect';
|
||||
import { timelineActions } from '../../../../timelines/store/timeline';
|
||||
import { State } from '../../../../common/store';
|
||||
|
||||
const LoadingChart = styled(EuiLoadingChart)`
|
||||
display: block;
|
||||
|
@ -55,7 +58,6 @@ interface PreviewHistogramProps {
|
|||
previewId: string;
|
||||
addNoiseWarning: () => void;
|
||||
spaceId: string;
|
||||
threshold?: FieldValueThreshold;
|
||||
ruleType: Type;
|
||||
index: string[];
|
||||
}
|
||||
|
@ -67,10 +69,10 @@ export const PreviewHistogram = ({
|
|||
previewId,
|
||||
addNoiseWarning,
|
||||
spaceId,
|
||||
threshold,
|
||||
ruleType,
|
||||
index,
|
||||
}: PreviewHistogramProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const { setQuery, isInitializing } = useGlobalTime();
|
||||
const { timelines: timelinesUi, cases } = useKibana().services;
|
||||
const from = useMemo(() => `now-1${timeFrame}`, [timeFrame]);
|
||||
|
@ -78,34 +80,36 @@ export const PreviewHistogram = ({
|
|||
const startDate = useMemo(() => formatDate(from), [from]);
|
||||
const endDate = useMemo(() => formatDate(to), [to]);
|
||||
const isEqlRule = useMemo(() => ruleType === 'eql', [ruleType]);
|
||||
const isThresholdRule = useMemo(() => ruleType === 'threshold', [ruleType]);
|
||||
const isMlRule = useMemo(() => ruleType === 'machine_learning', [ruleType]);
|
||||
|
||||
const [isLoading, { data, inspect, totalCount, refetch, buckets }] = usePreviewHistogram({
|
||||
const [isLoading, { data, inspect, totalCount, refetch }] = usePreviewHistogram({
|
||||
previewId,
|
||||
startDate,
|
||||
endDate,
|
||||
spaceId,
|
||||
threshold: isThresholdRule ? threshold : undefined,
|
||||
index,
|
||||
ruleType,
|
||||
});
|
||||
|
||||
const {
|
||||
columns,
|
||||
dataProviders,
|
||||
deletedEventIds,
|
||||
kqlMode,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
graphEventId,
|
||||
sort,
|
||||
} = alertsDefaultModel;
|
||||
timeline: {
|
||||
columns,
|
||||
dataProviders,
|
||||
defaultColumns,
|
||||
deletedEventIds,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
sort,
|
||||
} = alertsPreviewDefaultModel,
|
||||
} = useSelector((state: State) => eventsViewerSelector(state, TimelineId.rulePreview));
|
||||
|
||||
const {
|
||||
browserFields,
|
||||
docValueFields,
|
||||
indexPattern,
|
||||
runtimeMappings,
|
||||
dataViewId: selectedDataViewId,
|
||||
loading: isLoadingIndexPattern,
|
||||
} = useSourcererDataView(SourcererScopeName.detections);
|
||||
|
||||
|
@ -129,42 +133,28 @@ export const PreviewHistogram = ({
|
|||
}
|
||||
}, [setQuery, inspect, isLoading, isInitializing, refetch, previewId]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
timelineActions.createTimeline({
|
||||
columns,
|
||||
dataViewId: selectedDataViewId,
|
||||
defaultColumns,
|
||||
id: TimelineId.rulePreview,
|
||||
indexNames: [`${DEFAULT_PREVIEW_INDEX}-${spaceId}`],
|
||||
itemsPerPage,
|
||||
sort,
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const barConfig = useMemo(
|
||||
(): ChartSeriesConfigs => getHistogramConfig(endDate, startDate, !isEqlRule),
|
||||
[endDate, startDate, isEqlRule]
|
||||
);
|
||||
|
||||
const thresholdBarConfig = useMemo((): ChartSeriesConfigs => getThresholdHistogramConfig(), []);
|
||||
|
||||
const chartData = useMemo((): ChartSeriesData[] => [{ key: 'hits', value: data }], [data]);
|
||||
|
||||
const { thresholdChartData, thresholdTotalCount } = useMemo((): {
|
||||
thresholdChartData: ChartSeriesData[];
|
||||
thresholdTotalCount: number;
|
||||
} => {
|
||||
const total = buckets.length;
|
||||
const dataBuckets = buckets.map<{ x: string; y: number; g: string }>(
|
||||
({ key, doc_count: docCount }) => ({
|
||||
x: key,
|
||||
y: docCount,
|
||||
g: key,
|
||||
})
|
||||
);
|
||||
return {
|
||||
thresholdChartData: [{ key: 'hits', value: dataBuckets }],
|
||||
thresholdTotalCount: total,
|
||||
};
|
||||
}, [buckets]);
|
||||
|
||||
const subtitle = useMemo(
|
||||
(): string =>
|
||||
isLoading
|
||||
? i18n.QUERY_PREVIEW_SUBTITLE_LOADING
|
||||
: isThresholdRule
|
||||
? i18n.QUERY_PREVIEW_THRESHOLD_WITH_FIELD_TITLE(thresholdTotalCount)
|
||||
: i18n.QUERY_PREVIEW_TITLE(totalCount),
|
||||
[isLoading, totalCount, thresholdTotalCount, isThresholdRule]
|
||||
);
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
|
||||
return (
|
||||
|
@ -176,7 +166,6 @@ export const PreviewHistogram = ({
|
|||
id={`${ID}-${previewId}`}
|
||||
title={i18n.QUERY_GRAPH_HITS_TITLE}
|
||||
titleSize="xs"
|
||||
subtitle={subtitle}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={1}>
|
||||
|
@ -184,8 +173,8 @@ export const PreviewHistogram = ({
|
|||
<LoadingChart size="l" data-test-subj="preview-histogram-loading" />
|
||||
) : (
|
||||
<BarChart
|
||||
configs={isThresholdRule ? thresholdBarConfig : barConfig}
|
||||
barChart={isThresholdRule ? thresholdChartData : chartData}
|
||||
configs={barConfig}
|
||||
barChart={chartData}
|
||||
data-test-subj="preview-histogram-bar-chart"
|
||||
/>
|
||||
)}
|
||||
|
@ -194,7 +183,11 @@ export const PreviewHistogram = ({
|
|||
<>
|
||||
<EuiSpacer />
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>{i18n.QUERY_PREVIEW_DISCLAIMER_MAX_SIGNALS}</p>
|
||||
<p>
|
||||
{isMlRule
|
||||
? i18n.ML_PREVIEW_HISTOGRAM_DISCLAIMER
|
||||
: i18n.PREVIEW_HISTOGRAM_DISCLAIMER}
|
||||
</p>
|
||||
</EuiText>
|
||||
</>
|
||||
</EuiFlexItem>
|
||||
|
@ -213,13 +206,12 @@ export const PreviewHistogram = ({
|
|||
deletedEventIds,
|
||||
disabledCellActions: FIELDS_WITHOUT_CELL_ACTIONS,
|
||||
docValueFields,
|
||||
end: to,
|
||||
entityType: 'alerts',
|
||||
end: endDate,
|
||||
entityType: 'events',
|
||||
filters: [],
|
||||
globalFullScreen,
|
||||
graphEventId,
|
||||
hasAlertsCrud: false,
|
||||
id: TimelineId.detectionsPage,
|
||||
id: TimelineId.rulePreview,
|
||||
indexNames: [`${DEFAULT_PREVIEW_INDEX}-${spaceId}`],
|
||||
indexPattern,
|
||||
isLive: false,
|
||||
|
@ -233,7 +225,7 @@ export const PreviewHistogram = ({
|
|||
runtimeMappings,
|
||||
setQuery: () => {},
|
||||
sort,
|
||||
start: from,
|
||||
start: startDate,
|
||||
tGridEventRenderedViewEnabled,
|
||||
type: 'embedded',
|
||||
leadingControlColumns: getPreviewTableControlColumn(1.5),
|
||||
|
@ -242,11 +234,11 @@ export const PreviewHistogram = ({
|
|||
</FullScreenContainer>
|
||||
<DetailsPanel
|
||||
browserFields={browserFields}
|
||||
entityType={'alerts'}
|
||||
entityType={'events'}
|
||||
docValueFields={docValueFields}
|
||||
isFlyoutView
|
||||
runtimeMappings={runtimeMappings}
|
||||
timelineId={TimelineId.detectionsPage}
|
||||
timelineId={TimelineId.rulePreview}
|
||||
isReadOnly
|
||||
/>
|
||||
</CasesContext>
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* Copyright 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 { cloneDeep } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
|
||||
import { mockBrowserFields } from '../../../../common/containers/source/mock';
|
||||
import { DragDropContextWrapper } from '../../../../common/components/drag_and_drop/drag_drop_context_wrapper';
|
||||
import { defaultHeaders, mockTimelineData, TestProviders } from '../../../../common/mock';
|
||||
import { PreviewTableCellRenderer } from './preview_table_cell_renderer';
|
||||
import { getColumnRenderer } from '../../../../timelines/components/timeline/body/renderers/get_column_renderer';
|
||||
import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper';
|
||||
import { BrowserFields } from '../../../../../../timelines/common/search_strategy';
|
||||
import { Ecs } from '../../../../../common/ecs';
|
||||
import { columnRenderers } from '../../../../timelines/components/timeline/body/renderers';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../../../../timelines/components/timeline/body/renderers/get_column_renderer');
|
||||
|
||||
const getColumnRendererMock = getColumnRenderer as jest.Mock;
|
||||
const mockImplementation = {
|
||||
renderColumn: jest.fn(),
|
||||
};
|
||||
|
||||
describe('PreviewTableCellRenderer', () => {
|
||||
const columnId = '@timestamp';
|
||||
const eventId = '_id-123';
|
||||
const isExpandable = true;
|
||||
const isExpanded = true;
|
||||
const linkValues = ['foo', 'bar', '@baz'];
|
||||
const rowIndex = 3;
|
||||
const colIndex = 0;
|
||||
const setCellProps = jest.fn();
|
||||
const timelineId = 'test';
|
||||
const ecsData = {} as Ecs;
|
||||
const browserFields = {} as BrowserFields;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
getColumnRendererMock.mockImplementation(() => mockImplementation);
|
||||
});
|
||||
|
||||
test('it invokes `getColumnRenderer` with the expected arguments', () => {
|
||||
const data = cloneDeep(mockTimelineData[0].data);
|
||||
const header = cloneDeep(defaultHeaders[0]);
|
||||
const isDetails = true;
|
||||
|
||||
mount(
|
||||
<TestProviders>
|
||||
<DragDropContextWrapper browserFields={mockBrowserFields}>
|
||||
<DroppableWrapper droppableId="testing">
|
||||
<PreviewTableCellRenderer
|
||||
browserFields={browserFields}
|
||||
columnId={columnId}
|
||||
data={data}
|
||||
ecsData={ecsData}
|
||||
eventId={eventId}
|
||||
header={header}
|
||||
isDetails={isDetails}
|
||||
isDraggable={true}
|
||||
isExpandable={isExpandable}
|
||||
isExpanded={isExpanded}
|
||||
linkValues={linkValues}
|
||||
rowIndex={rowIndex}
|
||||
colIndex={colIndex}
|
||||
setCellProps={setCellProps}
|
||||
timelineId={timelineId}
|
||||
/>
|
||||
</DroppableWrapper>
|
||||
</DragDropContextWrapper>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getColumnRenderer).toBeCalledWith(header.id, columnRenderers, data);
|
||||
});
|
||||
|
||||
test('if in tgrid expanded value, it invokes `renderColumn` with the expected arguments', () => {
|
||||
const data = cloneDeep(mockTimelineData[0].data);
|
||||
const header = cloneDeep(defaultHeaders[0]);
|
||||
const isDetails = true;
|
||||
const truncate = isDetails ? false : true;
|
||||
|
||||
mount(
|
||||
<TestProviders>
|
||||
<DragDropContextWrapper browserFields={mockBrowserFields}>
|
||||
<DroppableWrapper droppableId="testing">
|
||||
<PreviewTableCellRenderer
|
||||
browserFields={browserFields}
|
||||
columnId={columnId}
|
||||
data={data}
|
||||
ecsData={ecsData}
|
||||
eventId={eventId}
|
||||
header={header}
|
||||
isDetails={isDetails}
|
||||
isDraggable={true}
|
||||
isExpandable={isExpandable}
|
||||
isExpanded={isExpanded}
|
||||
linkValues={linkValues}
|
||||
rowIndex={rowIndex}
|
||||
colIndex={colIndex}
|
||||
setCellProps={setCellProps}
|
||||
timelineId={timelineId}
|
||||
truncate={truncate}
|
||||
/>
|
||||
</DroppableWrapper>
|
||||
</DragDropContextWrapper>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockImplementation.renderColumn).toBeCalledWith({
|
||||
asPlainText: false,
|
||||
browserFields,
|
||||
columnName: header.id,
|
||||
ecsData,
|
||||
eventId,
|
||||
field: header,
|
||||
isDetails,
|
||||
isDraggable: true,
|
||||
linkValues,
|
||||
rowRenderers: undefined,
|
||||
timelineId,
|
||||
truncate,
|
||||
values: ['2018-11-05T19:03:25.937Z'],
|
||||
});
|
||||
});
|
||||
|
||||
test('if in tgrid expanded value, it does not render any actions', () => {
|
||||
const data = cloneDeep(mockTimelineData[0].data);
|
||||
const header = cloneDeep(defaultHeaders[1]);
|
||||
const isDetails = true;
|
||||
const id = 'event.severity';
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<DragDropContextWrapper browserFields={mockBrowserFields}>
|
||||
<DroppableWrapper droppableId="testing">
|
||||
<PreviewTableCellRenderer
|
||||
browserFields={browserFields}
|
||||
columnId={id}
|
||||
ecsData={ecsData}
|
||||
data={data}
|
||||
eventId={eventId}
|
||||
header={header}
|
||||
isDetails={isDetails}
|
||||
isDraggable={true}
|
||||
isExpandable={isExpandable}
|
||||
isExpanded={isExpanded}
|
||||
linkValues={linkValues}
|
||||
rowIndex={rowIndex}
|
||||
colIndex={colIndex}
|
||||
setCellProps={setCellProps}
|
||||
timelineId={timelineId}
|
||||
/>
|
||||
</DroppableWrapper>
|
||||
</DragDropContextWrapper>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="data-grid-expanded-cell-value-actions"]').exists()
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
|
@ -89,6 +89,7 @@ export const PreviewTableCellRenderer: React.FC<CellValueElementProps> = ({
|
|||
const styledContentClassName = isDetails
|
||||
? 'eui-textBreakWord'
|
||||
: 'eui-displayInlineBlock eui-textTruncate';
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledContent className={styledContentClassName} $isDetails={isDetails}>
|
||||
|
|
|
@ -54,7 +54,7 @@ export const QUERY_PREVIEW_LABEL = i18n.translate(
|
|||
export const QUERY_PREVIEW_HELP_TEXT = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryPreviewHelpText',
|
||||
{
|
||||
defaultMessage: 'Select a timeframe of data to preview query results',
|
||||
defaultMessage: 'Select a timeframe of data to preview query results.',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -73,9 +73,9 @@ export const THRESHOLD_QUERY_GRAPH_COUNT = i18n.translate(
|
|||
);
|
||||
|
||||
export const QUERY_GRAPH_HITS_TITLE = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryGraphHitsTitle',
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryPreviewTitle',
|
||||
{
|
||||
defaultMessage: 'Hits',
|
||||
defaultMessage: 'Rule Preview',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -124,18 +124,25 @@ export const QUERY_PREVIEW_ERROR = i18n.translate(
|
|||
);
|
||||
|
||||
export const QUERY_PREVIEW_DISCLAIMER = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimer',
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryPreviewDisclaimer',
|
||||
{
|
||||
defaultMessage:
|
||||
'Note: This preview excludes effects of rule exceptions and timestamp overrides.',
|
||||
}
|
||||
);
|
||||
|
||||
export const QUERY_PREVIEW_DISCLAIMER_MAX_SIGNALS = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimerEql',
|
||||
export const PREVIEW_HISTOGRAM_DISCLAIMER = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.histogramDisclaimer',
|
||||
{
|
||||
defaultMessage:
|
||||
'Note: This preview excludes effects of rule exceptions and timestamp overrides, and is limited to 100 results.',
|
||||
'Note: Alerts with multiple event.category values will be counted more than once.',
|
||||
}
|
||||
);
|
||||
|
||||
export const ML_PREVIEW_HISTOGRAM_DISCLAIMER = i18n.translate(
|
||||
'xpack.securitySolution.detectionEngine.queryPreview.mlHistogramDisclaimer',
|
||||
{
|
||||
defaultMessage: 'Note: Alerts with multiple host.name values will be counted more than once.',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -13,14 +13,12 @@ import { getEsQueryConfig } from '../../../../../../../../src/plugins/data/commo
|
|||
import { useKibana } from '../../../../common/lib/kibana';
|
||||
import { QUERY_PREVIEW_ERROR } from './translations';
|
||||
import { DEFAULT_PREVIEW_INDEX } from '../../../../../common/constants';
|
||||
import { FieldValueThreshold } from '../threshold_input';
|
||||
|
||||
interface PreviewHistogramParams {
|
||||
previewId: string | undefined;
|
||||
endDate: string;
|
||||
startDate: string;
|
||||
spaceId: string;
|
||||
threshold?: FieldValueThreshold;
|
||||
index: string[];
|
||||
ruleType: Type;
|
||||
}
|
||||
|
@ -30,7 +28,6 @@ export const usePreviewHistogram = ({
|
|||
startDate,
|
||||
endDate,
|
||||
spaceId,
|
||||
threshold,
|
||||
index,
|
||||
ruleType,
|
||||
}: PreviewHistogramParams) => {
|
||||
|
@ -47,9 +44,8 @@ export const usePreviewHistogram = ({
|
|||
});
|
||||
|
||||
const stackByField = useMemo(() => {
|
||||
const stackByDefault = ruleType === 'machine_learning' ? 'host.name' : 'event.category';
|
||||
return threshold?.field[0] ?? stackByDefault;
|
||||
}, [threshold, ruleType]);
|
||||
return ruleType === 'machine_learning' ? 'host.name' : 'event.category';
|
||||
}, [ruleType]);
|
||||
|
||||
const matrixHistogramRequest = useMemo(() => {
|
||||
return {
|
||||
|
@ -60,11 +56,10 @@ export const usePreviewHistogram = ({
|
|||
indexNames: [`${DEFAULT_PREVIEW_INDEX}-${spaceId}`],
|
||||
stackByField,
|
||||
startDate,
|
||||
threshold,
|
||||
includeMissingData: false,
|
||||
skip: error != null,
|
||||
};
|
||||
}, [startDate, endDate, filterQuery, spaceId, error, threshold, stackByField]);
|
||||
}, [startDate, endDate, filterQuery, spaceId, error, stackByField]);
|
||||
|
||||
return useMatrixHistogramCombined(matrixHistogramRequest);
|
||||
};
|
||||
|
|
|
@ -16,25 +16,9 @@ import {
|
|||
|
||||
import * as i18n from '../../components/alerts_table/translations';
|
||||
|
||||
/**
|
||||
* columns implements a subset of `EuiDataGrid`'s `EuiDataGridColumn` interface,
|
||||
* plus additional TGrid column properties
|
||||
*/
|
||||
export const columns: Array<
|
||||
const baseColumns: Array<
|
||||
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
|
||||
> = [
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: '@timestamp',
|
||||
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH + 10,
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
displayAsText: i18n.ALERTS_HEADERS_RULE,
|
||||
id: 'kibana.alert.rule.name',
|
||||
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
linkField: 'kibana.alert.rule.uuid',
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
displayAsText: i18n.ALERTS_HEADERS_SEVERITY,
|
||||
|
@ -78,3 +62,36 @@ export const columns: Array<
|
|||
id: 'destination.ip',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* columns implements a subset of `EuiDataGrid`'s `EuiDataGridColumn` interface,
|
||||
* plus additional TGrid column properties
|
||||
*/
|
||||
export const columns: Array<
|
||||
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
|
||||
> = [
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: '@timestamp',
|
||||
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH + 10,
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
displayAsText: i18n.ALERTS_HEADERS_RULE,
|
||||
id: 'kibana.alert.rule.name',
|
||||
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
linkField: 'kibana.alert.rule.uuid',
|
||||
},
|
||||
...baseColumns,
|
||||
];
|
||||
|
||||
export const rulePreviewColumns: Array<
|
||||
Pick<EuiDataGridColumn, 'display' | 'displayAsText' | 'id' | 'initialWidth'> & ColumnHeaderOptions
|
||||
> = [
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: 'kibana.alert.original_time',
|
||||
initialWidth: DEFAULT_DATE_COLUMN_MIN_WIDTH + 10,
|
||||
},
|
||||
...baseColumns,
|
||||
];
|
||||
|
|
|
@ -167,4 +167,13 @@ describe('event details footer component', () => {
|
|||
);
|
||||
expect(wrapper.getByTestId('side-panel-flyout-footer')).toBeTruthy();
|
||||
});
|
||||
test("it doesn't render the take action dropdown when readOnly prop is passed", () => {
|
||||
const wrapper = render(
|
||||
<TestProviders>
|
||||
<EventDetailsPanel {...{ ...defaultProps, isReadOnly: true }} isFlyoutView={true} />
|
||||
</TestProviders>
|
||||
);
|
||||
const element = wrapper.queryByTestId('side-panel-flyout-footer');
|
||||
expect(element).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -322,6 +322,7 @@ export enum TimelineId {
|
|||
casePage = 'timeline-case',
|
||||
test = 'test', // Reserved for testing purposes
|
||||
alternateTest = 'alternateTest',
|
||||
rulePreview = 'rule-preview',
|
||||
}
|
||||
|
||||
export const TimelineIdLiteralRt = runtimeTypes.union([
|
||||
|
@ -333,6 +334,7 @@ export const TimelineIdLiteralRt = runtimeTypes.union([
|
|||
runtimeTypes.literal(TimelineId.networkPageExternalAlerts),
|
||||
runtimeTypes.literal(TimelineId.active),
|
||||
runtimeTypes.literal(TimelineId.test),
|
||||
runtimeTypes.literal(TimelineId.rulePreview),
|
||||
]);
|
||||
|
||||
export type TimelineIdLiteral = runtimeTypes.TypeOf<typeof TimelineIdLiteralRt>;
|
||||
|
|
|
@ -113,7 +113,7 @@ export interface TGridIntegratedProps {
|
|||
filterStatus?: AlertStatus;
|
||||
globalFullScreen: boolean;
|
||||
// If truthy, the graph viewer (Resolver) is showing
|
||||
graphEventId: string | undefined;
|
||||
graphEventId?: string;
|
||||
graphOverlay?: React.ReactNode;
|
||||
hasAlertsCrud: boolean;
|
||||
height?: number;
|
||||
|
|
|
@ -20726,9 +20726,6 @@
|
|||
"xpack.securitySolution.detectionEngine.pageTitle": "Moteur de détection",
|
||||
"xpack.securitySolution.detectionEngine.panelSubtitleShowing": "Affichant",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphCountLabel": "Compte",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimer": "Remarque : cet aperçu exclut les effets d'exceptions aux règles et les remplacements d'horodatages.",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimerEql": "Remarque : cet aperçu exclut les effets d'exceptions aux règles et les remplacements d'horodatages, et est limité à 100 résultats.",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphHitsTitle": "Résultats",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewError": "Erreur de récupération de l'aperçu",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewInspectTitle": "aperçu de la recherche",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewNoiseWarning": "Avertissement de bruit : cette règle peut générer beaucoup de bruit. Envisagez d'affiner votre recherche. La base est une progression linéaire comportant 1 alerte par heure.",
|
||||
|
|
|
@ -23586,9 +23586,6 @@
|
|||
"xpack.securitySolution.detectionEngine.pageTitle": "検出エンジン",
|
||||
"xpack.securitySolution.detectionEngine.panelSubtitleShowing": "表示中",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphCountLabel": "カウント",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimer": "注:このプレビューは、ルール例外とタイムスタンプオーバーライドの効果を除外します。",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimerEql": "注:このプレビューは、ルール例外とタイムスタンプオーバーライドの効果を除外します。結果は100件に制限されます。",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphHitsTitle": "ヒット数",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewError": "プレビュー取得エラー",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewInspectTitle": "クエリプレビュー",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewNoiseWarning": "ノイズ警告:このルールではノイズが多く生じる可能性があります。クエリを絞り込むことを検討してください。これは1時間ごとに1アラートという線形進行に基づいています。",
|
||||
|
|
|
@ -23613,9 +23613,6 @@
|
|||
"xpack.securitySolution.detectionEngine.pageTitle": "检测引擎",
|
||||
"xpack.securitySolution.detectionEngine.panelSubtitleShowing": "正在显示",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphCountLabel": "计数",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimer": "注意:此预览不包括规则例外和时间戳覆盖的影响。",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphDisclaimerEql": "注意:此预览不包括规则例外和时间戳覆盖的影响,且仅显示 100 个结果。",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphHitsTitle": "命中数",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewError": "提取预览时出错",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewInspectTitle": "查询预览",
|
||||
"xpack.securitySolution.detectionEngine.queryPreview.queryGraphPreviewNoiseWarning": "噪音警告:此规则可能会导致大量噪音。考虑缩小您的查询范围。这基于每小时 1 条告警的线性级数。",
|
||||
|
|
|
@ -116,12 +116,12 @@ export const getSimpleRule = (ruleId = 'rule-1', enabled = false): QueryCreateSc
|
|||
/**
|
||||
* This is a typical simple preview rule for testing that is easy for most basic testing
|
||||
* @param ruleId
|
||||
* @param enabled The number of times the rule will be run through the executors. Defaulted to 20,
|
||||
* @param enabled The number of times the rule will be run through the executors. Defaulted to 12,
|
||||
* the execution time for the default interval time of 5m.
|
||||
*/
|
||||
export const getSimplePreviewRule = (
|
||||
ruleId = 'preview-rule-1',
|
||||
invocationCount = 20
|
||||
invocationCount = 12
|
||||
): PreviewRulesSchema => ({
|
||||
name: 'Simple Rule Query',
|
||||
description: 'Simple Rule Query',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue