Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[8.x] [Security Solution][Serverless] - Improve security solution performance (#194241) #194588

Merged
merged 1 commit into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { EventsQueryTabBody, ALERTS_EVENTS_HISTOGRAM_ID } from './events_query_t
import { useGlobalFullScreen } from '../../containers/use_full_screen';
import { licenseService } from '../../hooks/use_license';
import { mockHistory } from '../../mock/router';
import { DEFAULT_EVENTS_STACK_BY_VALUE } from './histogram_configurations';

const mockGetDefaultControlColumn = jest.fn();
jest.mock('../../../timelines/components/timeline/body/control_columns', () => ({
Expand Down Expand Up @@ -144,7 +145,7 @@ describe('EventsQueryTabBody', () => {
);

expect(result.getByTestId('header-section-supplements').querySelector('select')?.value).toEqual(
'event.action'
DEFAULT_EVENTS_STACK_BY_VALUE
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_
import type { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
import * as i18n from './translations';

const DEFAULT_EVENTS_STACK_BY = 'event.action';
export const NO_BREAKDOWN_STACK_BY_VALUE = 'no_breakdown';

export const DEFAULT_EVENTS_STACK_BY_VALUE = NO_BREAKDOWN_STACK_BY_VALUE;

export const getSubtitleFunction =
(defaultNumberFormat: string, isAlert: boolean) => (totalCount: number) =>
Expand All @@ -20,6 +22,10 @@ export const getSubtitleFunction =
}`;

export const eventsStackByOptions: MatrixHistogramOption[] = [
{
text: i18n.EVENTS_GRAPH_NO_BREAKDOWN_TITLE,
value: NO_BREAKDOWN_STACK_BY_VALUE,
},
{
text: 'event.action',
value: 'event.action',
Expand All @@ -36,7 +42,8 @@ export const eventsStackByOptions: MatrixHistogramOption[] = [

export const eventsHistogramConfig: MatrixHistogramConfigs = {
defaultStackByOption:
eventsStackByOptions.find((o) => o.text === DEFAULT_EVENTS_STACK_BY) ?? eventsStackByOptions[0],
eventsStackByOptions.find((o) => o.value === DEFAULT_EVENTS_STACK_BY_VALUE) ??
eventsStackByOptions[0],
stackByOptions: eventsStackByOptions,
subtitle: undefined,
title: i18n.EVENTS_GRAPH_TITLE,
Expand All @@ -58,7 +65,7 @@ const DEFAULT_STACK_BY = 'event.module';

export const alertsHistogramConfig: MatrixHistogramConfigs = {
defaultStackByOption:
alertsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0],
alertsStackByOptions.find((o) => o.value === DEFAULT_STACK_BY) ?? alertsStackByOptions[0],
stackByOptions: alertsStackByOptions,
title: i18n.ALERTS_GRAPH_TITLE,
getLensAttributes: getExternalAlertLensAttributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,10 @@ export const SHOW_EXTERNAL_ALERTS = i18n.translate(
export const EVENTS_GRAPH_TITLE = i18n.translate('xpack.securitySolution.eventsGraphTitle', {
defaultMessage: 'Events',
});

export const EVENTS_GRAPH_NO_BREAKDOWN_TITLE = i18n.translate(
'xpack.securitySolution.eventsHistogram.selectOptions.noBreakDownLabel',
{
defaultMessage: 'No breakdown',
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../visualization_actions/uti
import { VisualizationEmbeddable } from '../visualization_actions/visualization_embeddable';
import { useVisualizationResponse } from '../visualization_actions/use_visualization_response';
import type { SourcererScopeName } from '../../../sourcerer/store/model';
import { NO_BREAKDOWN_STACK_BY_VALUE } from '../events_tab/histogram_configurations';

export type MatrixHistogramComponentProps = MatrixHistogramQueryProps &
MatrixHistogramConfigs & {
Expand Down Expand Up @@ -165,6 +166,13 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
[isPtrIncluded, filterQuery]
);

// If the user selected the `No breakdown` option, we shouldn't perform the aggregation
const stackByField = useMemo(() => {
return selectedStackByOption.value === NO_BREAKDOWN_STACK_BY_VALUE
? undefined
: selectedStackByOption.value;
}, [selectedStackByOption.value]);

if (hideHistogram) {
return null;
}
Expand Down Expand Up @@ -216,7 +224,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
id={visualizationId}
inspectTitle={title as string}
lensAttributes={lensAttributes}
stackByField={selectedStackByOption.value}
stackByField={stackByField}
timerange={timerange}
/>
) : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { GetLensAttributes, LensAttributes } from '../visualization_actions

export interface MatrixHistogramOption {
text: string;
value: string;
value: string | undefined;
}

export type GetSubTitle = (count: number) => string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { wrapper } from '../../mocks';

import { useLensAttributes } from '../../use_lens_attributes';

import { getEventsHistogramLensAttributes } from './events';
import { getEventsHistogramLensAttributes, stackByFieldAccessorId } from './events';

jest.mock('uuid', () => ({
v4: jest.fn().mockReturnValue('0039eb0c-9a1a-4687-ae54-0f4e239bec75'),
Expand Down Expand Up @@ -497,4 +497,41 @@ describe('getEventsHistogramLensAttributes', () => {
})
);
});

it('should render the layer for the stackByField when provided', () => {
const { result } = renderHook(
() =>
useLensAttributes({
getLensAttributes: getEventsHistogramLensAttributes,
stackByField: 'event.dataset',
}),
{ wrapper }
);

expect(result?.current?.state?.visualization).toEqual(
expect.objectContaining({
layers: expect.arrayContaining([
expect.objectContaining({ splitAccessor: stackByFieldAccessorId }),
]),
})
);
});

it('should not render the layer for the stackByField is undefined', () => {
const { result } = renderHook(
() =>
useLensAttributes({
getLensAttributes: getEventsHistogramLensAttributes,
}),
{ wrapper }
);

expect(result?.current?.state?.visualization).toEqual(
expect.objectContaining({
layers: expect.arrayContaining([
expect.not.objectContaining({ splitAccessor: stackByFieldAccessorId }),
]),
})
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { v4 as uuidv4 } from 'uuid';
import type { GetLensAttributes } from '../../types';

const layerId = uuidv4();
// Exported for testing purposes
export const stackByFieldAccessorId = '34919782-4546-43a5-b668-06ac934d3acd';

export const getEventsHistogramLensAttributes: GetLensAttributes = (
stackByField = 'event.action',
stackByField,
extraOptions = {}
) => {
return {
Expand All @@ -37,7 +39,7 @@ export const getEventsHistogramLensAttributes: GetLensAttributes = (
showGridlines: false,
layerType: 'data',
xAccessor: 'aac9d7d0-13a3-480a-892b-08207a787926',
splitAccessor: '34919782-4546-43a5-b668-06ac934d3acd',
splitAccessor: stackByField ? stackByFieldAccessorId : undefined,
},
],
yRightExtent: {
Expand Down Expand Up @@ -83,30 +85,32 @@ export const getEventsHistogramLensAttributes: GetLensAttributes = (
sourceField: '___records___',
params: { emptyAsNull: true },
},
'34919782-4546-43a5-b668-06ac934d3acd': {
label: `Top values of ${stackByField}`,
dataType: 'string',
operationType: 'terms',
scale: 'ordinal',
sourceField: `${stackByField}`,
isBucketed: true,
params: {
size: 10,
orderBy: {
type: 'column',
columnId: 'e09e0380-0740-4105-becc-0a4ca12e3944',
},
orderDirection: 'desc',
otherBucket: true,
missingBucket: false,
parentFormat: {
id: 'terms',
...(stackByField && {
[stackByFieldAccessorId]: {
label: `Top values of ${stackByField}`,
dataType: 'string',
operationType: 'terms',
scale: 'ordinal',
sourceField: `${stackByField}`,
isBucketed: true,
params: {
size: 10,
orderBy: {
type: 'column',
columnId: 'e09e0380-0740-4105-becc-0a4ca12e3944',
},
orderDirection: 'desc',
otherBucket: true,
missingBucket: false,
parentFormat: {
id: 'terms',
},
},
},
},
}),
},
columnOrder: [
'34919782-4546-43a5-b668-06ac934d3acd',
...(stackByField ? [stackByFieldAccessorId] : []),
'aac9d7d0-13a3-480a-892b-08207a787926',
'e09e0380-0740-4105-becc-0a4ca12e3944',
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { useSourcererDataView } from '../../../sourcerer/containers';
import { kpiHostMetricLensAttributes } from './lens_attributes/hosts/kpi_host_metric';
import { useRouteSpy } from '../../utils/route/use_route_spy';
import { SecurityPageName } from '../../../app/types';
import { getEventsHistogramLensAttributes } from './lens_attributes/common/events';

jest.mock('../../../sourcerer/containers');
jest.mock('../../utils/route/use_route_spy', () => ({
Expand Down Expand Up @@ -212,6 +213,25 @@ describe('useLensAttributes', () => {
]);
});

it('should not set splitAccessor if stackByField is undefined', () => {
const { result } = renderHook(
() =>
useLensAttributes({
getLensAttributes: getEventsHistogramLensAttributes,
stackByField: undefined,
}),
{ wrapper }
);

expect(result?.current?.state?.visualization).toEqual(
expect.objectContaining({
layers: expect.arrayContaining([
expect.objectContaining({ seriesType: 'bar_stacked', splitAccessor: undefined }),
]),
})
);
});

it('should return null if no indices exist', () => {
(useSourcererDataView as jest.Mock).mockReturnValue({
dataViewId: 'security-solution-default',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const useLensAttributes = ({
() =>
lensAttributes ??
((getLensAttributes &&
stackByField &&
stackByField !== null &&
getLensAttributes(stackByField, extraOptions)) as LensAttributes),
[extraOptions, getLensAttributes, lensAttributes, stackByField]
);
Expand All @@ -82,7 +82,7 @@ export const useLensAttributes = ({
const lensAttrsWithInjectedData = useMemo(() => {
if (
lensAttributes == null &&
(getLensAttributes == null || stackByField == null || stackByField?.length === 0)
(getLensAttributes == null || stackByField === null || stackByField?.length === 0)
) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { useKibana, useUiSetting$ } from '../../../common/lib/kibana';
import {
eventsStackByOptions,
eventsHistogramConfig,
NO_BREAKDOWN_STACK_BY_VALUE,
} from '../../../common/components/events_tab/histogram_configurations';
import { HostsTableType } from '../../../explore/hosts/store/model';
import type { GlobalTimeArgs } from '../../../common/containers/use_global_time';
Expand All @@ -36,7 +37,7 @@ import { useFormatUrl } from '../../../common/components/link_to';
import { useInvalidFilterQuery } from '../../../common/hooks/use_invalid_filter_query';
import type { SourcererScopeName } from '../../../sourcerer/store/model';

const DEFAULT_STACK_BY = 'event.dataset';
const DEFAULT_STACK_BY = NO_BREAKDOWN_STACK_BY_VALUE;

const ID = 'eventsByDatasetOverview';
const CHART_HEIGHT = 160;
Expand Down Expand Up @@ -156,7 +157,7 @@ const EventsByDatasetComponent: React.FC<Props> = ({
defaultStackByOption:
onlyField != null
? getHistogramOption(onlyField)
: eventsStackByOptions.find((o) => o.text === DEFAULT_STACK_BY) ??
: eventsStackByOptions.find((o) => o.value === DEFAULT_STACK_BY) ??
eventsStackByOptions[0],
legendPosition: Position.Right,
subtitle: (totalCount: number) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,11 @@ const TestComponent = (props: Partial<ComponentProps<typeof QueryTabContent>>) =

const dispatch = useDispatch();

// populating timeline so that it is not blank
useEffect(() => {
// Unified field list can be a culprit for long load times, so we wait for the timeline to be interacted with to load
dispatch(timelineActions.showTimeline({ id: TimelineId.test, show: true }));

// populating timeline so that it is not blank
dispatch(
timelineActions.applyKqlFilterQuery({
id: TimelineId.test,
Expand Down
Loading