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

Added categories for log types #741

Merged
merged 2 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 21 additions & 0 deletions public/components/Utility/LogCategoryOption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiTitle } from '@elastic/eui';
import React, { useEffect, useRef } from 'react';

export const LogCategoryOptionView: React.FC<{ categoryName: string }> = ({ categoryName }) => {
const inputRef = useRef<HTMLHeadingElement | null>(null);

useEffect(() => {
inputRef.current?.closest('button.euiFilterSelectItem')?.setAttribute('disabled', 'true');
}, [inputRef.current]);

return (
<EuiTitle size="xxs">
<h4 ref={inputRef}>{categoryName}</h4>
</EuiTitle>
);
};
14 changes: 9 additions & 5 deletions public/pages/Correlations/containers/CreateCorrelationRule.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { CoreServicesContext } from '../../../components/core_services';
import { RouteComponentProps, useParams } from 'react-router-dom';
import { validateName } from '../../../utils/validation';
import { FieldMappingService, IndexService } from '../../../services';
import { errorNotificationToast } from '../../../utils/helpers';
import { errorNotificationToast, getLogTypeOptions } from '../../../utils/helpers';

export interface CreateCorrelationRuleProps {
indexService: IndexService;
Expand Down Expand Up @@ -103,6 +103,7 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
...correlationRuleStateDefaultValue,
});
const [action, setAction] = useState<CorrelationRuleAction>('Create');
const [logTypeOptions, setLogTypeOptions] = useState<any[]>([]);

useEffect(() => {
if (props.history.location.state?.rule) {
Expand All @@ -119,6 +120,12 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
setAction('Edit');
setInitialRuleValues();
}

const setupLogTypeOptions = async () => {
const options = await getLogTypeOptions();
setLogTypeOptions(options);
};
setupLogTypeOptions();
}, []);

const submit = async (values: any) => {
Expand Down Expand Up @@ -274,10 +281,7 @@ export const CreateCorrelationRule: React.FC<CreateCorrelationRuleProps> = (
isInvalid={isInvalidInputForQuery('logType')}
placeholder="Select a log type"
data-test-subj={'rule_type_dropdown'}
options={ruleTypes.map(({ label }) => ({
value: label.toLowerCase(),
label,
}))}
options={logTypeOptions}
singleSelection={{ asPlainText: true }}
onChange={(e) => {
props.handleChange(`queries[${queryIdx}].logType`)(
Expand Down
8 changes: 2 additions & 6 deletions public/pages/Correlations/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import {
CorrelationRuleQuery,
} from '../../../../types';
import { Search } from '@opensearch-project/oui/src/eui_components/basic_table';
import { ruleTypes } from '../../Rules/utils/constants';
import { FieldClause } from '@opensearch-project/oui/src/eui_components/search_bar/query/ast';
import { formatRuleType } from '../../../utils/helpers';
import { formatRuleType, getLogTypeFilterOptions } from '../../../utils/helpers';

export const getCorrelationRulesTableColumns = (
onRuleNameClick: (rule: CorrelationRule) => void,
Expand Down Expand Up @@ -110,10 +109,7 @@ export const getCorrelationRulesTableSearchConfig = (
field: 'logTypes',
name: 'Log Types',
multiSelect: 'or',
options: ruleTypes.map(({ value, label }) => ({
value,
name: label,
})),
options: getLogTypeFilterOptions(),
},
],
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { RuleItem } from '../DetectionRules/types/interfaces';
import { ruleTypes } from '../../../../../Rules/utils/constants';
import ConfigureFieldMapping from '../../../ConfigureFieldMapping';
import { ConfigureFieldMappingProps } from '../../../ConfigureFieldMapping/containers/ConfigureFieldMapping';
import { getLogTypeOptions } from '../../../../../../utils/helpers';

interface DetectorTypeProps {
detectorType: string;
Expand All @@ -30,18 +31,23 @@ interface DetectorTypeState {
}

export default class DetectorType extends Component<DetectorTypeProps, DetectorTypeState> {
private detectorTypeOptions: { value: string; label: string }[];
private detectorTypeOptions: { value: string; label: string }[] = [];
constructor(props: DetectorTypeProps) {
super(props);

this.detectorTypeOptions = ruleTypes.map(({ label }) => ({ value: label, label }));
const detectorTypeIds = this.detectorTypeOptions.map((option) => option.value);
this.state = {
fieldTouched: false,
detectorTypeIds,
detectorTypeIds: [],
};
}

async componentDidMount(): Promise<void> {
this.detectorTypeOptions = await getLogTypeOptions();
this.setState({
detectorTypeIds: ruleTypes.map((option) => option.value),
});
}

onChange = (detectorType: string) => {
this.setState({ fieldTouched: true });
this.props.onDetectorTypeChange(detectorType);
Expand All @@ -66,7 +72,13 @@ export default class DetectorType extends Component<DetectorTypeProps, DetectorT
const { detectorType } = this.props;

return (
<ContentPanel title={'Detection'} titleSize={'m'}>
<ContentPanel
title={'Detection'}
subTitleText={
'The available detection rules are automatically populated based on your selected log type. Use the toggles to select detection rules for this detector'
}
titleSize={'m'}
>
<EuiFormRow
label={
<div>
Expand Down
8 changes: 3 additions & 5 deletions public/pages/Detectors/containers/Detectors/Detectors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
capitalizeFirstLetter,
errorNotificationToast,
formatRuleType,
getLogTypeFilterOptions,
renderTime,
} from '../../../../utils/helpers';
import { CoreServicesContext } from '../../../../components/core_services';
Expand Down Expand Up @@ -298,11 +299,11 @@ export default class Detectors extends Component<DetectorsProps, DetectorsState>
detectorHits.map((detector) => (detector._source.enabled ? 'Active' : 'Inactive'))
),
];
const logType = [...new Set(detectorHits.map((detector) => detector._source.detector_type))];
const search = {
box: {
placeholder: 'Search threat detectors',
schema: true,
incremental: true,
},
filters: [
{
Expand All @@ -319,10 +320,7 @@ export default class Detectors extends Component<DetectorsProps, DetectorsState>
type: 'field_value_selection',
field: 'logType',
name: 'Log type',
options: logType.map((logType) => ({
value: logType,
name: formatRuleType(logType),
})),
options: getLogTypeFilterOptions(),
multiSelect: 'or',
} as FieldValueSelectionFilterConfigType,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
search={
Object {
"box": Object {
"incremental": true,
"placeholder": "Search threat detectors",
"schema": true,
},
Expand All @@ -598,12 +599,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
"field": "logType",
"multiSelect": "or",
"name": "Log type",
"options": Array [
Object {
"name": "-",
"value": "detector_type",
},
],
"options": Array [],
"type": "field_value_selection",
},
],
Expand All @@ -628,6 +624,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
<EuiSearchBar
box={
Object {
"incremental": true,
"placeholder": "Search threat detectors",
"schema": Object {
"fields": Object {
Expand Down Expand Up @@ -669,12 +666,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
"field": "logType",
"multiSelect": "or",
"name": "Log type",
"options": Array [
Object {
"name": "-",
"value": "detector_type",
},
],
"options": Array [],
"type": "field_value_selection",
},
]
Expand All @@ -697,18 +689,18 @@ exports[`<Detectors /> spec renders the component 1`] = `
className="euiFlexItem euiSearchBar__searchHolder"
>
<EuiSearchBox
incremental={false}
incremental={true}
isInvalid={false}
onSearch={[Function]}
placeholder="Search threat detectors"
query=""
>
<EuiFieldSearch
aria-label="This is a search bar. After typing your query, hit enter to filter the results lower in the page."
aria-label="This is a search bar. As you type, the results lower in the page will automatically filter."
compressed={false}
defaultValue=""
fullWidth={true}
incremental={false}
incremental={true}
inputRef={[Function]}
isClearable={true}
isInvalid={false}
Expand All @@ -732,7 +724,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
isInvalid={false}
>
<input
aria-label="This is a search bar. After typing your query, hit enter to filter the results lower in the page."
aria-label="This is a search bar. As you type, the results lower in the page will automatically filter."
className="euiFieldSearch euiFieldSearch--fullWidth"
defaultValue=""
onKeyUp={[Function]}
Expand Down Expand Up @@ -800,12 +792,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
"field": "logType",
"multiSelect": "or",
"name": "Log type",
"options": Array [
Object {
"name": "-",
"value": "detector_type",
},
],
"options": Array [],
"type": "field_value_selection",
},
]
Expand Down Expand Up @@ -975,12 +962,7 @@ exports[`<Detectors /> spec renders the component 1`] = `
"field": "logType",
"multiSelect": "or",
"name": "Log type",
"options": Array [
Object {
"name": "-",
"value": "detector_type",
},
],
"options": Array [],
"type": "field_value_selection",
}
}
Expand Down
36 changes: 31 additions & 5 deletions public/pages/LogTypes/components/LogTypeForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import {
EuiFlexItem,
EuiFormRow,
EuiSpacer,
EuiSuperSelect,
EuiTextArea,
} from '@elastic/eui';
import { LogTypeItem } from '../../../../types';
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';

export interface LogTypeFormProps {
logTypeDetails: LogTypeItem;
Expand All @@ -40,17 +42,21 @@ export const LogTypeForm: React.FC<LogTypeFormProps> = ({
onConfirm,
}) => {
const [nameError, setNameError] = useState('');
const [categoryError, setCategoryError] = useState('');
const [categoryTouched, setCategoryTouched] = useState(false);

const updateErrors = (details = logTypeDetails) => {
const updateErrors = (details: LogTypeItem, onSubmit = false) => {
const nameInvalid = !validateName(details.name, LOG_TYPE_NAME_REGEX, false /* shouldTrim */);
amsiglan marked this conversation as resolved.
Show resolved Hide resolved
const categoryInvalid = (categoryTouched || onSubmit) && !details.category;
setNameError(nameInvalid ? 'Invalid name' : '');
setCategoryError(categoryInvalid ? 'Select category to assign' : '');

return { nameInvalid };
return { nameInvalid, categoryInvalid };
};
const onConfirmClicked = () => {
const { nameInvalid } = updateErrors();
const { nameInvalid, categoryInvalid } = updateErrors(logTypeDetails, true);

if (nameInvalid) {
if (nameInvalid || categoryInvalid) {
notifications?.toasts.addDanger({
title: `Failed to ${confirmButtonText.toLowerCase()}`,
text: `Fix the marked errors.`,
Expand Down Expand Up @@ -83,7 +89,6 @@ export const LogTypeForm: React.FC<LogTypeFormProps> = ({
setLogTypeDetails(newLogType);
updateErrors(newLogType);
}}
placeholder="Enter name for the log type"
readOnly={!isEditMode}
disabled={isEditMode && !!logTypeDetails.detectionRulesCount}
/>
Expand Down Expand Up @@ -111,6 +116,27 @@ export const LogTypeForm: React.FC<LogTypeFormProps> = ({
readOnly={!isEditMode}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow label="Category" isInvalid={!!categoryError} error={categoryError}>
<EuiSuperSelect
options={logTypeCategoryOptions.map((option) => ({
...option,
disabled: !isEditMode || (isEditMode && !!logTypeDetails.detectionRulesCount),
}))}
valueOfSelected={logTypeDetails?.category}
onChange={(value) => {
const newLogType = {
...logTypeDetails,
category: value,
};
setCategoryTouched(true);
setLogTypeDetails(newLogType);
updateErrors(newLogType);
}}
hasDividers
itemLayoutAlign="top"
></EuiSuperSelect>
</EuiFormRow>
{isEditMode ? (
<EuiBottomBar>
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd">
Expand Down
1 change: 1 addition & 0 deletions public/pages/LogTypes/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export const defaultLogType: LogTypeBase = {
description: '',
source: 'Custom',
tags: null,
category: '',
};
17 changes: 17 additions & 0 deletions public/pages/LogTypes/utils/helpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +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';

export const getLogTypesTableColumns = (
showDetails: (id: string) => void,
Expand All @@ -27,6 +28,11 @@ export const getLogTypesTableColumns = (
name: 'Description',
truncateText: false,
},
{
field: 'category',
name: 'Category',
truncateText: false,
},
{
field: 'source',
name: 'Source',
Expand Down Expand Up @@ -60,6 +66,17 @@ export const getLogTypesTableSearchConfig = (): Search => {
schema: true,
},
filters: [
{
type: 'field_value_selection',
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)),
},
{
type: 'field_value_selection',
field: 'source',
Expand Down
Loading