Skip to content

Commit

Permalink
add rule snooze settings on the rule details page
Browse files Browse the repository at this point in the history
  • Loading branch information
maximpn committed Apr 20, 2023
1 parent 0cf7fd1 commit 543fbe5
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 74 deletions.
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import React, { useMemo } from 'react';
import { useUserData } from '../../detections/components/user_info';
import { hasUserCRUDPermission } from '../../common/utils/privileges';
import { useKibana } from '../../common/lib/kibana';
import type { RuleSnoozeSettings } from '../rule_management/logic';
import { useInvalidateFetchRulesSnoozeSettingsQuery } from '../rule_management/api/hooks/use_fetch_rules_snooze_settings';

interface RuleSnoozeBadgeProps {
/**
* Rule's snooze settings, when set to `undefined` considered as a loading state
*/
snoozeSettings: RuleSnoozeSettings | undefined;
/**
* It should represent a user readable error message happened during data snooze settings fetching
*/
error?: string;
showTooltipInline?: boolean;
}

export function RuleSnoozeBadge({
snoozeSettings,
error,
showTooltipInline = false,
}: RuleSnoozeBadgeProps): JSX.Element {
const RulesListNotifyBadge = useKibana().services.triggersActionsUi.getRulesListNotifyBadge;
const [{ canUserCRUD }] = useUserData();
const hasCRUDPermissions = hasUserCRUDPermission(canUserCRUD);
const invalidateFetchRuleSnoozeSettings = useInvalidateFetchRulesSnoozeSettingsQuery();
const isLoading = !snoozeSettings;
const rule = useMemo(() => {
return {
id: snoozeSettings?.id ?? '',
muteAll: snoozeSettings?.mute_all ?? false,
activeSnoozes: snoozeSettings?.active_snoozes ?? [],
isSnoozedUntil: snoozeSettings?.is_snoozed_until
? new Date(snoozeSettings.is_snoozed_until)
: undefined,
snoozeSchedule: snoozeSettings?.snooze_schedule,
isEditable: hasCRUDPermissions,
};
}, [snoozeSettings, hasCRUDPermissions]);

if (error) {
return (
<EuiToolTip content={error}>
<EuiButtonIcon size="s" iconType="bellSlash" disabled />
</EuiToolTip>
);
}

return (
<RulesListNotifyBadge
rule={rule}
isLoading={isLoading}
showTooltipInline={showTooltipInline}
onRuleChanged={invalidateFetchRuleSnoozeSettings}
/>
);
}
@@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { useFetchRulesSnoozeSettings } from '../../../../../rule_management/api/hooks/use_fetch_rules_snooze_settings';
import { RuleSnoozeBadge } from '../../../../../components/rule_snooze_settings';
import * as i18n from './translations';

interface RuleDetailsSnoozeBadge {
/**
* Rule's SO id (not ruleId)
*/
id: string;
}

export function RuleDetailsSnoozeSettings({ id }: RuleDetailsSnoozeBadge): JSX.Element {
const { data: [snoozeSettings] = [undefined], isError: isSnoozeSettingsFetchError } =
useFetchRulesSnoozeSettings([id]);

return (
<RuleSnoozeBadge
snoozeSettings={snoozeSettings}
error={isSnoozeSettingsFetchError ? i18n.UNABLE_TO_FETCH_RULE_SNOOZE_SETTINGS : undefined}
showTooltipInline={true}
/>
);
}
Expand Up @@ -8,7 +8,7 @@
import { i18n } from '@kbn/i18n';

export const UNABLE_TO_FETCH_RULE_SNOOZE_SETTINGS = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleManagement.ruleSnoozeBadge.error.unableToFetch',
'xpack.securitySolution.detectionEngine.ruleDetails.rulesSnoozeSettings.error.unableToFetch',
{
defaultMessage: 'Unable to fetch snooze settings',
}
Expand Down
Expand Up @@ -86,6 +86,11 @@ jest.mock('react-router-dom', () => {
};
});

// RuleDetailsSnoozeSettings is an isolated component and not essential for existing tests
jest.mock('./components/rule_details_snooze_settings', () => ({
RuleDetailsSnoozeSettings: () => <></>,
}));

const mockRedirectLegacyUrl = jest.fn();
const mockGetLegacyUrlConflict = jest.fn();
jest.mock('../../../../common/lib/kibana', () => {
Expand Down
Expand Up @@ -140,6 +140,7 @@ import { EditRuleSettingButtonLink } from '../../../../detections/pages/detectio
import { useStartMlJobs } from '../../../rule_management/logic/use_start_ml_jobs';
import { useBulkDuplicateExceptionsConfirmation } from '../../../rule_management_ui/components/rules_table/bulk_actions/use_bulk_duplicate_confirmation';
import { BulkActionDuplicateExceptionsConfirmation } from '../../../rule_management_ui/components/rules_table/bulk_actions/bulk_duplicate_exceptions_confirmation';
import { RuleDetailsSnoozeSettings } from './components/rule_details_snooze_settings';

/**
* Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space.
Expand Down Expand Up @@ -544,18 +545,23 @@ const RuleDetailsPageComponent: React.FC<DetectionEngineComponentProps> = ({
<EuiLoadingSpinner size="m" data-test-subj="rule-status-loader" />
</EuiFlexItem>
) : (
<RuleStatus status={lastExecutionStatus} date={lastExecutionDate}>
<EuiButtonIcon
data-test-subj="refreshButton"
color="primary"
onClick={refreshRule}
iconType="refresh"
aria-label={ruleI18n.REFRESH}
isDisabled={!isExistingRule}
/>
</RuleStatus>
<>
<RuleStatus status={lastExecutionStatus} date={lastExecutionDate}>
<EuiButtonIcon
data-test-subj="refreshButton"
color="primary"
onClick={refreshRule}
iconType="refresh"
aria-label={ruleI18n.REFRESH}
isDisabled={!isExistingRule}
/>
</RuleStatus>
<EuiFlexItem grow={false}>
<RuleDetailsSnoozeSettings id={ruleId} />
</EuiFlexItem>
</>
);
}, [lastExecutionStatus, lastExecutionDate, ruleLoading, isExistingRule, refreshRule]);
}, [ruleId, lastExecutionStatus, lastExecutionDate, ruleLoading, isExistingRule, refreshRule]);

const ruleError = useMemo(() => {
return ruleLoading ? (
Expand Down

This file was deleted.

Expand Up @@ -21,3 +21,10 @@ export const ML_RULE_JOBS_WARNING_BUTTON_LABEL = i18n.translate(
defaultMessage: 'Visit rule details page to investigate',
}
);

export const UNABLE_TO_FETCH_RULES_SNOOZE_SETTINGS = i18n.translate(
'xpack.securitySolution.detectionEngine.ruleManagement.rulesSnoozeSettings.error.unableToFetch',
{
defaultMessage: 'Unable to fetch snooze settings',
}
);
Expand Up @@ -22,7 +22,7 @@ import type {
} from '../../../../../common/detection_engine/rule_monitoring';
import { isMlRule } from '../../../../../common/machine_learning/helpers';
import { getEmptyTagValue } from '../../../../common/components/empty_value';
import { RuleSnoozeBadge } from '../../../rule_management/components/rule_snooze_badge';
import { RuleSnoozeBadge } from '../../../components/rule_snooze_settings';
import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date';
import { SecuritySolutionLinkAnchor } from '../../../../common/components/links';
import { getRuleDetailsTabUrl } from '../../../../common/components/link_to/redirect_to_detection_engine';
Expand All @@ -46,6 +46,7 @@ import { useHasActionsPrivileges } from './use_has_actions_privileges';
import { useHasMlPermissions } from './use_has_ml_permissions';
import { useRulesTableActions } from './use_rules_table_actions';
import { MlRuleWarningPopover } from './ml_rule_warning_popover';
import * as rulesTableI18n from './translations';

export type TableColumn = EuiBasicTableColumn<Rule> | EuiTableActionsColumnType<Rule>;

Expand Down Expand Up @@ -108,15 +109,28 @@ const useEnabledColumn = ({ hasCRUDPermissions, startMlJobs }: ColumnsProps): Ta
};

const useRuleSnoozeColumn = (): TableColumn => {
const {
state: { rulesSnoozeSettings },
} = useRulesTableContext();

return useMemo(
() => ({
field: 'snooze',
name: i18n.COLUMN_SNOOZE,
render: (_, rule: Rule) => <RuleSnoozeBadge id={rule.id} />,
render: (_, rule: Rule) => (
<RuleSnoozeBadge
snoozeSettings={rulesSnoozeSettings.data[rule.id]}
error={
rulesSnoozeSettings.isError
? rulesTableI18n.UNABLE_TO_FETCH_RULES_SNOOZE_SETTINGS
: undefined
}
/>
),
width: '100px',
sortable: false,
}),
[]
[rulesSnoozeSettings]
);
};

Expand Down

0 comments on commit 543fbe5

Please sign in to comment.