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

[Security] Adds field mapping support to rule creation Part II #71402

Merged
merged 7 commits into from
Jul 14, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export const severity_mapping_item = t.exact(
severity,
})
);
export type SeverityMappingItem = t.TypeOf<typeof severity_mapping_item>;

export const severity_mapping = t.array(severity_mapping_item);
export type SeverityMapping = t.TypeOf<typeof severity_mapping>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface OperatorProps {
isLoading: boolean;
isDisabled: boolean;
isClearable: boolean;
fieldTypeFilter?: string[];
fieldInputWidth?: number;
onChange: (a: IFieldType[]) => void;
}
Expand All @@ -28,13 +29,22 @@ export const FieldComponent: React.FC<OperatorProps> = ({
isLoading = false,
isDisabled = false,
isClearable = false,
fieldTypeFilter = [],
fieldInputWidth = 190,
onChange,
}): JSX.Element => {
const getLabel = useCallback((field): string => field.name, []);
const optionsMemo = useMemo((): IFieldType[] => (indexPattern ? indexPattern.fields : []), [
indexPattern,
]);
const optionsMemo = useMemo((): IFieldType[] => {
if (indexPattern != null) {
if (fieldTypeFilter.length > 0) {
return indexPattern.fields.filter((f) => fieldTypeFilter.includes(f.type));
} else {
return indexPattern.fields;
}
} else {
return [];
}
}, [fieldTypeFilter, indexPattern]);
const selectedOptionsMemo = useMemo((): IFieldType[] => (selectedField ? [selectedField] : []), [
selectedField,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface AutocompleteFieldMatchProps {
isLoading: boolean;
isDisabled: boolean;
isClearable: boolean;
fieldInputWidth?: number;
onChange: (arg: string) => void;
}

Expand All @@ -33,6 +34,7 @@ export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchPro
isLoading,
isDisabled = false,
isClearable = false,
fieldInputWidth,
onChange,
}): JSX.Element => {
const [isLoadingSuggestions, suggestions, updateSuggestions] = useFieldValueAutocomplete({
Expand Down Expand Up @@ -97,6 +99,7 @@ export const AutocompleteFieldMatchComponent: React.FC<AutocompleteFieldMatchPro
isInvalid={!isValid}
sortMatchesBy="startsWith"
data-test-subj="valuesAutocompleteComboBox matchComboxBox"
style={fieldInputWidth ? { width: `${fieldInputWidth}px` } : {}}
fullWidth
async
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export { UtilityBar } from './utility_bar';
export { UtilityBarAction } from './utility_bar_action';
export { UtilityBarGroup } from './utility_bar_group';
export { UtilityBarSection } from './utility_bar_section';
export { UtilityBarSpacer } from './utility_bar_spacer';
export { UtilityBarText } from './utility_bar_text';
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export interface BarProps {
border?: boolean;
}

export interface BarSectionProps {
grow?: boolean;
}

export interface BarGroupProps {
grow?: boolean;
}

export const Bar = styled.aside.attrs({
className: 'siemUtilityBar',
})<BarProps>`
Expand All @@ -36,8 +44,8 @@ Bar.displayName = 'Bar';

export const BarSection = styled.div.attrs({
className: 'siemUtilityBar__section',
})`
${({ theme }) => css`
})<BarSectionProps>`
${({ grow, theme }) => css`
& + & {
margin-top: ${theme.eui.euiSizeS};
}
Expand All @@ -53,14 +61,18 @@ export const BarSection = styled.div.attrs({
margin-left: ${theme.eui.euiSize};
}
}
${grow &&
css`
flex: 1;
`}
`}
`;
BarSection.displayName = 'BarSection';

export const BarGroup = styled.div.attrs({
className: 'siemUtilityBar__group',
})`
${({ theme }) => css`
})<BarGroupProps>`
${({ grow, theme }) => css`
align-items: flex-start;
display: flex;
flex-wrap: wrap;
Expand Down Expand Up @@ -93,6 +105,10 @@ export const BarGroup = styled.div.attrs({
margin-right: 0;
}
}
${grow &&
css`
flex: 1;
`}
`}
`;
BarGroup.displayName = 'BarGroup';
Expand All @@ -118,3 +134,12 @@ export const BarAction = styled.div.attrs({
`}
`;
BarAction.displayName = 'BarAction';

export const BarSpacer = styled.div.attrs({
className: 'siemUtilityBar__spacer',
})`
${() => css`
flex: 1;
`}
`;
BarSpacer.displayName = 'BarSpacer';
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

import React from 'react';

import { BarGroup } from './styles';
import { BarGroup, BarGroupProps } from './styles';

export interface UtilityBarGroupProps {
export interface UtilityBarGroupProps extends BarGroupProps {
children: React.ReactNode;
}

export const UtilityBarGroup = React.memo<UtilityBarGroupProps>(({ children }) => (
<BarGroup>{children}</BarGroup>
export const UtilityBarGroup = React.memo<UtilityBarGroupProps>(({ grow, children }) => (
<BarGroup grow={grow}>{children}</BarGroup>
));

UtilityBarGroup.displayName = 'UtilityBarGroup';
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

import React from 'react';

import { BarSection } from './styles';
import { BarSection, BarSectionProps } from './styles';

export interface UtilityBarSectionProps {
export interface UtilityBarSectionProps extends BarSectionProps {
children: React.ReactNode;
}

export const UtilityBarSection = React.memo<UtilityBarSectionProps>(({ children }) => (
<BarSection>{children}</BarSection>
export const UtilityBarSection = React.memo<UtilityBarSectionProps>(({ grow, children }) => (
<BarSection grow={grow}>{children}</BarSection>
));

UtilityBarSection.displayName = 'UtilityBarSection';
Original file line number Diff line number Diff line change
@@ -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;
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

import { BarSpacer } from './styles';

export interface UtilityBarSpacerProps {
dataTestSubj?: string;
}

export const UtilityBarSpacer = React.memo<UtilityBarSpacerProps>(({ dataTestSubj }) => (
<BarSpacer data-test-subj={dataTestSubj} />
));

UtilityBarSpacer.displayName = 'UtilityBarSpacer';
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ describe('AlertsUtilityBar', () => {
currentFilter="closed"
selectAll={jest.fn()}
showClearSelection={true}
showBuildingBlockAlerts={false}
onShowBuildingBlockAlertsChanged={jest.fn()}
updateAlertsStatus={jest.fn()}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { isEmpty } from 'lodash/fp';
import React, { useCallback } from 'react';
import numeral from '@elastic/numeral';

import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiCheckbox } from '@elastic/eui';
import styled from 'styled-components';

import { Status } from '../../../../../common/detection_engine/schemas/common/schemas';
import { Link } from '../../../../common/components/link_icon';
import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants';
Expand All @@ -18,6 +19,7 @@ import {
UtilityBarAction,
UtilityBarGroup,
UtilityBarSection,
UtilityBarSpacer,
UtilityBarText,
} from '../../../../common/components/utility_bar';
import * as i18n from './translations';
Expand All @@ -34,6 +36,8 @@ interface AlertsUtilityBarProps {
currentFilter: Status;
selectAll: () => void;
selectedEventIds: Readonly<Record<string, TimelineNonEcsData[]>>;
showBuildingBlockAlerts: boolean;
onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void;
showClearSelection: boolean;
totalCount: number;
updateAlertsStatus: UpdateAlertsStatus;
Expand All @@ -52,6 +56,8 @@ const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
selectedEventIds,
currentFilter,
selectAll,
showBuildingBlockAlerts,
onShowBuildingBlockAlertsChanged,
showClearSelection,
updateAlertsStatus,
}) => {
Expand Down Expand Up @@ -125,17 +131,36 @@ const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
</UtilityBarFlexGroup>
);

const UtilityBarAdditionalFiltersContent = (closePopover: () => void) => (
<UtilityBarFlexGroup direction="column">
<EuiFlexItem>
<EuiCheckbox
id="showBuildingBlockAlertsCheckbox"
aria-label="showBuildingBlockAlerts"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
closePopover();
onShowBuildingBlockAlertsChanged(e.target.checked);
}}
checked={showBuildingBlockAlerts}
color="text"
data-test-subj="showBuildingBlockAlertsCheckbox"
label={i18n.ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK}
/>
</EuiFlexItem>
</UtilityBarFlexGroup>
);

return (
<>
<UtilityBar>
<UtilityBarSection>
<UtilityBarSection grow={true}>
<UtilityBarGroup>
<UtilityBarText dataTestSubj="showingAlerts">
{i18n.SHOWING_ALERTS(formattedTotalCount, totalCount)}
</UtilityBarText>
</UtilityBarGroup>

<UtilityBarGroup>
<UtilityBarGroup grow={true}>
{canUserCRUD && hasIndexWrite && (
<>
<UtilityBarText dataTestSubj="selectedAlerts">
Expand Down Expand Up @@ -174,6 +199,17 @@ const AlertsUtilityBarComponent: React.FC<AlertsUtilityBarProps> = ({
</UtilityBarAction>
</>
)}
<UtilityBarSpacer />
<UtilityBarAction
dataTestSubj="additionalFilters"
disabled={areEventsLoading}
iconType="arrowDown"
iconSide="right"
ownFocus={false}
popoverContent={UtilityBarAdditionalFiltersContent}
>
{i18n.ADDITIONAL_FILTERS_ACTIONS}
</UtilityBarAction>
</UtilityBarGroup>
</UtilityBarSection>
</UtilityBar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ export const SELECT_ALL_ALERTS = (totalAlertsFormatted: string, totalAlerts: num
'Select all {totalAlertsFormatted} {totalAlerts, plural, =1 {alert} other {alerts}}',
});

export const ADDITIONAL_FILTERS_ACTIONS = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersTitle',
{
defaultMessage: 'Additional filters',
}
);

export const ADDITIONAL_FILTERS_ACTIONS_SHOW_BUILDING_BLOCK = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.additionalFiltersActions.showBuildingBlockTitle',
{
defaultMessage: 'Include building block alerts',
}
);

export const CLEAR_SELECTION = i18n.translate(
'xpack.securitySolution.detectionEngine.alerts.utilityBar.clearSelectionTitle',
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ export const buildAlertsRuleIdFilter = (ruleId: string): Filter[] => [
},
];

export const buildShowBuildingBlockFilter = (showBuildingBlockAlerts: boolean): Filter[] => [
...(showBuildingBlockAlerts
? []
: [
{
meta: {
alias: null,
negate: true,
disabled: false,
type: 'exists',
key: 'signal.rule.building_block_type',
value: 'exists',
},
// @ts-ignore TODO: Rework parent typings to support ExistsFilter[]
exists: { field: 'signal.rule.building_block_type' },
},
]),
];

export const alertsHeaders: ColumnHeaderOptions[] = [
{
columnHeaderType: defaultColumnHeaderType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ describe('AlertsTableComponent', () => {
clearEventsLoading={jest.fn()}
setEventsDeleted={jest.fn()}
clearEventsDeleted={jest.fn()}
showBuildingBlockAlerts={false}
onShowBuildingBlockAlertsChanged={jest.fn()}
updateTimelineIsLoading={jest.fn()}
updateTimeline={jest.fn()}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ interface OwnProps {
hasIndexWrite: boolean;
from: number;
loading: boolean;
showBuildingBlockAlerts: boolean;
onShowBuildingBlockAlertsChanged: (showBuildingBlockAlerts: boolean) => void;
signalsIndex: string;
to: number;
}
Expand Down Expand Up @@ -94,6 +96,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
selectedEventIds,
setEventsDeleted,
setEventsLoading,
showBuildingBlockAlerts,
onShowBuildingBlockAlertsChanged,
signalsIndex,
to,
updateTimeline,
Expand Down Expand Up @@ -302,6 +306,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
currentFilter={filterGroup}
selectAll={selectAllCallback}
selectedEventIds={selectedEventIds}
showBuildingBlockAlerts={showBuildingBlockAlerts}
onShowBuildingBlockAlertsChanged={onShowBuildingBlockAlertsChanged}
showClearSelection={showClearSelectionAction}
totalCount={totalCount}
updateAlertsStatus={updateAlertsStatusCallback.bind(null, refetchQuery)}
Expand All @@ -313,6 +319,8 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
hasIndexWrite,
clearSelectionCallback,
filterGroup,
showBuildingBlockAlerts,
onShowBuildingBlockAlertsChanged,
loadingEventIds.length,
selectAllCallback,
selectedEventIds,
Expand Down
Loading