Skip to content

Commit

Permalink
[Custom log types] CRUD operations for log types (opensearch-project#675
Browse files Browse the repository at this point in the history
)

* log types table; details page with basic editing

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

* implemented create, delete of log type

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

* detection rules; delete modals added

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

* updated snapshots

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

* addressed PR comments

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

* updated cypress workflow

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

* updated detector cypress test

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

* addressed PR comments

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

---------

Signed-off-by: Amardeepsingh Siglani <[email protected]>
  • Loading branch information
amsiglan committed Jul 31, 2023
1 parent c4df3d2 commit c81643c
Show file tree
Hide file tree
Showing 14 changed files with 720 additions and 35 deletions.
115 changes: 115 additions & 0 deletions public/pages/LogTypes/components/DeleteLogTypeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import {
EuiButton,
EuiCallOut,
EuiConfirmModal,
EuiFieldText,
EuiForm,
EuiFormRow,
EuiLoadingSpinner,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiOverlayMask,
EuiSpacer,
} from '@elastic/eui';
import React from 'react';
import { useState } from 'react';

export interface DeleteLogTypeModalProps {
logTypeName: string;
detectionRulesCount: number;
loading?: boolean;
closeModal: () => void;
onConfirm: () => void;
}

export const DeleteLogTypeModal: React.FC<DeleteLogTypeModalProps> = ({
detectionRulesCount,
logTypeName,
loading,
closeModal,
onConfirm,
}) => {
const [confirmDeleteText, setConfirmDeleteText] = useState('');

if (loading) {
return (
<EuiOverlayMask>
<EuiModal onClose={closeModal}>
<EuiLoadingSpinner size="l" />
</EuiModal>
</EuiOverlayMask>
);
}

const onConfirmClick = () => {
onConfirm();
closeModal();
};

return (
<EuiOverlayMask>
{detectionRulesCount > 0 ? (
<EuiModal onClose={closeModal}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<h1>This log type can't be deleted</h1>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiCallOut
size="s"
title={`This log type is associated with ${detectionRulesCount} detection ${
detectionRulesCount > 1 ? 'rules' : 'rule'
}.`}
iconType={'iInCircle'}
color="warning"
/>
<EuiSpacer />
<p>
Only log types that don’t have any associated rules can be deleted. Consider editing
log type or deleting associated detection rules.
</p>
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={closeModal} fill>
Close
</EuiButton>
</EuiModalFooter>
</EuiModal>
) : (
<EuiConfirmModal
title={`Delete log type?`}
onCancel={closeModal}
onConfirm={onConfirmClick}
cancelButtonText={'Cancel'}
confirmButtonText={`Delete log type`}
buttonColor={'danger'}
defaultFocusedButton="confirm"
confirmButtonDisabled={confirmDeleteText != logTypeName}
>
<EuiForm>
<p>The log type will be permanently deleted. This action is irreversible.</p>
<EuiSpacer size="s" />
<p style={{ marginBottom: '0.3rem' }}>
Type <b>{logTypeName}</b> to confirm
</p>
<EuiFormRow>
<EuiFieldText
value={confirmDeleteText}
onChange={(e) => setConfirmDeleteText(e.target.value)}
/>
</EuiFormRow>
</EuiForm>
</EuiConfirmModal>
)}
</EuiOverlayMask>
);
};
72 changes: 72 additions & 0 deletions public/pages/LogTypes/components/LogTypeDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiButton, EuiDescriptionList } from '@elastic/eui';
import { ContentPanel } from '../../../components/ContentPanel';
import React from 'react';
import { LogTypeItem } from '../../../../types';
import { DataStore } from '../../../store/DataStore';
import { LogTypeForm } from './LogTypeForm';
import { NotificationsStart } from 'opensearch-dashboards/public';

export interface LogTypeDetailsProps {
initialLogTypeDetails: LogTypeItem;
logTypeDetails: LogTypeItem;
isEditMode: boolean;
notifications: NotificationsStart;
setIsEditMode: (isEdit: boolean) => void;
setLogTypeDetails: (logType: LogTypeItem) => void;
}

export const LogTypeDetails: React.FC<LogTypeDetailsProps> = ({
initialLogTypeDetails,
logTypeDetails,
isEditMode,
notifications,
setIsEditMode,
setLogTypeDetails,
}) => {
const onUpdateLogType = async () => {
const success = await DataStore.logTypes.updateLogType(logTypeDetails);
if (success) {
setIsEditMode(false);
}
};

return (
<ContentPanel
title="Details"
actions={
!isEditMode &&
logTypeDetails.source.toLocaleLowerCase() !== 'sigma' && [
<EuiButton onClick={() => setIsEditMode(true)}>Edit</EuiButton>,
]
}
>
<EuiDescriptionList
type="column"
listItems={[
{
title: 'Log type',
description: (
<LogTypeForm
logTypeDetails={logTypeDetails}
isEditMode={isEditMode}
confirmButtonText={'Update'}
notifications={notifications}
setLogTypeDetails={setLogTypeDetails}
onCancel={() => {
setLogTypeDetails(initialLogTypeDetails);
setIsEditMode(false);
}}
onConfirm={onUpdateLogType}
/>
),
},
]}
/>
</ContentPanel>
);
};
26 changes: 24 additions & 2 deletions public/pages/LogTypes/components/LogTypeDetectionRules.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,30 @@
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { RulesTable } from '../../Rules/components/RulesTable/RulesTable';
import { RuleTableItem } from '../../Rules/utils/helpers';
import { ContentPanel } from '../../../components/ContentPanel';
import { EuiButton } from '@elastic/eui';

export interface LogTypeDetectionRulesProps {
logTypeId: string;
rules: RuleTableItem[];
loadingRules: boolean;
refreshRules: () => void;
}

export const LogTypeDetectionRules = () => {};
export const LogTypeDetectionRules: React.FC<LogTypeDetectionRulesProps> = ({
rules,
loadingRules,
refreshRules,
}) => {
return (
<ContentPanel
title="Detection rules"
hideHeaderBorder={true}
actions={[<EuiButton onClick={refreshRules}>Refresh</EuiButton>]}
>
<RulesTable loading={loadingRules} ruleItems={rules} showRuleDetails={() => {}} />
</ContentPanel>
);
};
124 changes: 124 additions & 0 deletions public/pages/LogTypes/components/LogTypeForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import {
EuiBottomBar,
EuiButton,
EuiButtonEmpty,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiSpacer,
EuiTextArea,
} from '@elastic/eui';
import { LogTypeItem } from '../../../../types';
import React from 'react';
import { validateName } from '../../../utils/validation';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { useState } from 'react';

export interface LogTypeFormProps {
logTypeDetails: LogTypeItem;
isEditMode: boolean;
confirmButtonText: string;
notifications: NotificationsStart;
setLogTypeDetails: (logType: LogTypeItem) => void;
onCancel: () => void;
onConfirm: () => void;
}

export const LogTypeForm: React.FC<LogTypeFormProps> = ({
logTypeDetails,
isEditMode,
confirmButtonText,
notifications,
setLogTypeDetails,
onCancel,
onConfirm,
}) => {
const [nameError, setNameError] = useState('');

const updateErrors = (details = logTypeDetails) => {
const nameInvalid = !validateName(details.name);
setNameError(nameInvalid ? 'Invalid name' : '');

return { nameInvalid };
};
const onConfirmClicked = () => {
const { nameInvalid } = updateErrors();

if (nameInvalid) {
notifications?.toasts.addDanger({
title: `Failed to ${confirmButtonText.toLowerCase()}`,
text: `Fix the marked errors.`,
toastLifeTimeMs: 3000,
});

return;
}
onConfirm();
};

return (
<>
<EuiFormRow
label="Name"
helpText={
isEditMode &&
'Must contain 5-50 characters. Valid characters are a-z, A-Zm 0-9, hyphens, spaces, and underscores'
}
isInvalid={!!nameError}
error={nameError}
>
<EuiFieldText
value={logTypeDetails?.name}
onChange={(e) => {
const newLogType = {
...logTypeDetails!,
name: e.target.value,
};
setLogTypeDetails(newLogType);
updateErrors(newLogType);
}}
placeholder="Enter name for the log type"
disabled={!isEditMode || !!logTypeDetails.detectionRules}
/>
</EuiFormRow>
<EuiSpacer />
<EuiFormRow label="Description">
<EuiTextArea
value={logTypeDetails?.description}
onChange={(e) => {
const newLogType = {
...logTypeDetails!,
description: e.target.value,
};
setLogTypeDetails(newLogType);
updateErrors(newLogType);
}}
placeholder="Description of the log type"
disabled={!isEditMode}
/>
</EuiFormRow>
{isEditMode ? (
<EuiBottomBar>
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="ghost" size="s" iconType="cross" onClick={onCancel}>
Cancel
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton color="primary" fill iconType="check" size="s" onClick={onConfirmClicked}>
{confirmButtonText}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiBottomBar>
) : null}
</>
);
};
Loading

0 comments on commit c81643c

Please sign in to comment.