Skip to content

Commit

Permalink
Enhance log type filters correlations (#745) (#746)
Browse files Browse the repository at this point in the history
* updated log type filters; fixed search field

Signed-off-by: Amardeepsingh Siglani <[email protected]>

* updated log type filters; fixed search field

Signed-off-by: Amardeepsingh Siglani <[email protected]>

* fixed typo

Signed-off-by: Amardeepsingh Siglani <[email protected]>

---------

Signed-off-by: Amardeepsingh Siglani <[email protected]>
(cherry picked from commit b581e58)

Co-authored-by: Amardeepsingh Siglani <[email protected]>
  • Loading branch information
opensearch-trigger-bot[bot] and amsiglan authored Oct 6, 2023
1 parent 5d3afed commit b7b9ee2
Show file tree
Hide file tree
Showing 10 changed files with 171 additions and 80 deletions.
93 changes: 78 additions & 15 deletions public/pages/Correlations/components/FilterGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,32 @@ import {
EuiPopoverTitle,
EuiFieldSearch,
FilterChecked,
EuiPopoverFooter,
EuiButtonEmpty,
} from '@elastic/eui';

export type FilterItem = { name: string | React.ReactNode; id: string; checked?: FilterChecked };
export type FilterItem = {
name: string | React.ReactNode;
id: string;
visible: boolean;
childOptionIds?: Set<string>;
checked?: FilterChecked;
};
export interface LogTypeFilterGroupProps {
groupName: string;
items: FilterItem[];
hasGroupOptions?: boolean;
hasFooter?: boolean;
setItems: (items: FilterItem[]) => void;
}

export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({ groupName, items, setItems }) => {
export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({
groupName,
items,
hasGroupOptions,
hasFooter,
setItems,
}) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [showActiveFilters, setShowActiveFilters] = useState(false);

Expand All @@ -33,7 +49,7 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({ groupName, item
setIsPopoverOpen(false);
};

function updateItem(index: number) {
function toggleItem(index: number) {
if (!items[index]) {
return;
}
Expand All @@ -54,7 +70,35 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({ groupName, item
checked: 'on',
};
}
const childIds = newItems[index].childOptionIds;
if (childIds) {
newItems.forEach((item) => {
if (childIds.has(item.id)) {
item.checked = newItems[index].checked;
}
});
}

setItems(newItems);
setShowActiveFilters(true);
}

function toggleAll(state: 'on' | undefined) {
const newItems = items.map((item) => ({
...item,
checked: state,
}));

setItems(newItems);
setShowActiveFilters(!!state);
}

function search(term: string) {
const newItems = [...items];
term = term.toLowerCase();
items.forEach((item) => {
item.visible = item.id.toLowerCase().includes(term);
});
setItems(newItems);
setShowActiveFilters(true);
}
Expand Down Expand Up @@ -83,20 +127,39 @@ export const FilterGroup: React.FC<LogTypeFilterGroupProps> = ({ groupName, item
panelPaddingSize="none"
>
<EuiPopoverTitle paddingSize="s">
<EuiFieldSearch compressed />
<EuiFieldSearch compressed onSearch={search} />
</EuiPopoverTitle>
<div className="ouiFilterSelect__items">
{items.map((item, index) => (
<EuiFilterSelectItem
checked={item.checked}
key={index}
onClick={() => updateItem(index)}
showIcons={true}
>
{item.name}
</EuiFilterSelectItem>
))}
<div
className="ouiFilterSelect__items"
style={hasFooter ? { maxHeight: 400, overflow: 'scroll' } : undefined}
>
{items.map((item, index) => {
const itemStyle: any = {};
itemStyle['paddingLeft'] =
hasGroupOptions && !item.childOptionIds ? 20 : itemStyle['paddingLeft'];
itemStyle['display'] = !item.visible ? 'none' : itemStyle['display'];

return (
<EuiFilterSelectItem
checked={item.checked}
key={index}
onClick={() => toggleItem(index)}
showIcons={true}
style={itemStyle}
>
{item.name}
</EuiFilterSelectItem>
);
})}
</div>
{hasFooter && (
<EuiPopoverFooter>
<div>
<EuiButtonEmpty onClick={() => toggleAll('on')}>Select all</EuiButtonEmpty>
<EuiButtonEmpty onClick={() => toggleAll(undefined)}>Deselect all</EuiButtonEmpty>
</div>
</EuiPopoverFooter>
)}
</EuiPopover>
</EuiFilterGroup>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,8 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
<FilterGroup
groupName="Log types"
items={this.state.logTypeFilterOptions}
hasGroupOptions={true}
hasFooter={true}
setItems={this.onLogTypeFilterChange}
/>
</EuiFilterGroup>
Expand Down
39 changes: 32 additions & 7 deletions public/pages/Correlations/utils/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import React from 'react';
import { CorrelationGraphData } from '../../../../types';
import { ruleSeverity, ruleTypes } from '../../Rules/utils/constants';
import { FilterItem } from '../components/FilterGroup';
import { EuiIcon } from '@elastic/eui';
import { EuiIcon, EuiTitle } from '@elastic/eui';
import { logTypeCategories, logTypesByCategories } from '../../../utils/constants';
import _ from 'lodash';

export const graphRenderOptions = {
nodes: {
Expand Down Expand Up @@ -47,12 +49,34 @@ export const graphRenderOptions = {
},
};

export const getDefaultLogTypeFilterItemOptions: () => FilterItem[] = () =>
Object.values(ruleTypes).map((type) => ({
name: `${type.label}`,
id: type.label.toLowerCase(),
checked: 'on',
}));
export const getDefaultLogTypeFilterItemOptions: () => FilterItem[] = () => {
const options: FilterItem[] = [];
logTypeCategories.forEach((category) => {
const logTypes = logTypesByCategories[category];
options.push({
name: (
<EuiTitle size="xxs">
<h4>{category}</h4>
</EuiTitle>
),
id: category,
checked: 'on',
childOptionIds: new Set(logTypes.map(({ name }) => name)),
visible: true,
});

logTypes.forEach(({ name }) => {
options.push({
name: _.capitalize(name),
id: name,
checked: 'on',
visible: true,
});
});
});

return options;
};

export const defaultSeverityFilterItemOptions: FilterItem[] = Object.values(ruleSeverity).map(
(sev) => {
Expand All @@ -64,6 +88,7 @@ export const defaultSeverityFilterItemOptions: FilterItem[] = Object.values(rule
),
id: sev.value,
checked: 'on',
visible: true,
};
}
);
Expand Down
4 changes: 2 additions & 2 deletions public/pages/LogTypes/components/LogTypeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import React from 'react';
import { LOG_TYPE_NAME_REGEX, validateName } from '../../../utils/validation';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { useState } from 'react';
import { logTypeCategoryOptions } from '../../../utils/constants';
import { getLogTypeCategoryOptions } from '../../../utils/helpers';

export interface LogTypeFormProps {
logTypeDetails: LogTypeItem;
Expand Down Expand Up @@ -119,7 +119,7 @@ export const LogTypeForm: React.FC<LogTypeFormProps> = ({
<EuiSpacer />
<EuiFormRow label="Category" isInvalid={!!categoryError} error={categoryError}>
<EuiSuperSelect
options={logTypeCategoryOptions.map((option) => ({
options={getLogTypeCategoryOptions().map((option) => ({
...option,
disabled: !isEditMode || (isEditMode && !!logTypeDetails.detectionRulesCount),
}))}
Expand Down
10 changes: 4 additions & 6 deletions public/pages/LogTypes/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { LogType } from '../../../../types';
import { capitalize } from 'lodash';
import { Search } from '@opensearch-project/oui/src/eui_components/basic_table';
import { ruleSource } from '../../Rules/utils/constants';
import { logTypesByCategories } from '../../../utils/constants';
import { logTypeCategories } from '../../../utils/constants';

export const getLogTypesTableColumns = (
showDetails: (id: string) => void,
Expand Down Expand Up @@ -71,11 +71,9 @@ export const getLogTypesTableSearchConfig = (): Search => {
field: 'category',
name: 'Category',
multiSelect: 'or',
options: Object.keys(logTypesByCategories)
.map((category) => ({
value: category,
}))
.sort((a, b) => (a.value < b.value ? -1 : a.value > b.value ? 1 : 0)),
options: logTypeCategories.map((category) => ({
value: category,
})),
},
{
type: 'field_value_selection',
Expand Down
3 changes: 0 additions & 3 deletions public/store/CorrelationsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,6 @@ export class CorrelationsStore implements ICorrelationsStore {
if (findingRes.ok) {
findingRes.response.findings.forEach((f) => {
const rule = allRules.find((rule) => rule._id === f.queries[0].id);
if (!rule) {
console.log(f.queries[0].id);
}
findings[f.id] = {
...f,
id: f.id,
Expand Down
16 changes: 15 additions & 1 deletion public/store/LogTypeStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LogTypeService from '../services/LogTypeService';
import { errorNotificationToast } from '../utils/helpers';
import { DataStore } from './DataStore';
import { ruleTypes } from '../pages/Rules/utils/constants';
import { logTypesByCategories } from '../utils/constants';
import { logTypeCategories, logTypesByCategories } from '../utils/constants';

export class LogTypeStore {
constructor(private service: LogTypeService, private notifications: NotificationsStart) {}
Expand Down Expand Up @@ -73,6 +73,20 @@ export class LogTypeStore {
logTypesByCategories[logType.category] = logTypesByCategories[logType.category] || [];
logTypesByCategories[logType.category].push(logType);
});
logTypeCategories.splice(
0,
logTypeCategories.length,
...Object.keys(logTypesByCategories).sort((a, b) => {
if (a === 'Other') {
return 1;
} else if (b === 'Other') {
return -1;
} else {
return a < b ? -1 : a > b ? 1 : 0;
}
})
);

return logTypes;
}

Expand Down
18 changes: 2 additions & 16 deletions public/utils/constants.tsx → public/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { SimpleSavedObject } from 'opensearch-dashboards/public';
import { Detector, LogType, ServerResponse } from '../../types';
import { DetectorInput, PeriodSchedule } from '../../models/interfaces';
import { DetectorHit } from '../../server/models/interfaces';
import { EuiText } from '@elastic/eui';
import _ from 'lodash';

export const DATE_MATH_FORMAT = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
Expand Down Expand Up @@ -177,7 +175,7 @@ export const pendingDashboardCreations: {
[detectorId: string]: undefined | Promise<void | ServerResponse<SimpleSavedObject<unknown>>>;
} = {};

const logTypeCategoryInfo = [
export const logTypeCategoryDescription: { name: string; description: string }[] = [
{ name: 'Access Management', description: 'User access, authentication, group management' },
{ name: 'Applications', description: 'Application lifecycle, API, and web resources activities' },
{ name: 'Cloud Services', description: 'Services managed by cloud providers' },
Expand All @@ -186,17 +184,5 @@ const logTypeCategoryInfo = [
{ name: 'Other', description: 'Logs not covered in other categories' },
];

export const logTypeCategoryOptions: any[] = logTypeCategoryInfo.map(({ name, description }) => ({
value: name,
inputDisplay: name,
dropdownDisplay: (
<>
<strong>{name}</strong>
<EuiText size="s" color="subdued">
<p className="ouiTextColor--subdued">{description}</p>
</EuiText>
</>
),
}));

export const logTypeCategories: string[] = [];
export const logTypesByCategories: { [category: string]: LogType[] } = {};
Loading

0 comments on commit b7b9ee2

Please sign in to comment.