Skip to content

Commit

Permalink
Merge branch 'main' into detection-rules/8.6-0600b575
Browse files Browse the repository at this point in the history
  • Loading branch information
terrancedejesus authored Nov 28, 2022
2 parents 9ca1dcb + 13c1b0b commit 9f287bb
Show file tree
Hide file tree
Showing 20 changed files with 953 additions and 186 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/security_solution/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ export const RULES_TABLE_PAGE_SIZE_OPTIONS = [5, 10, 20, 50, RULES_TABLE_MAX_PAG
* we will need to update these constants with the corresponding version.
*/
export const NEW_FEATURES_TOUR_STORAGE_KEYS = {
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.4',
RULE_MANAGEMENT_PAGE: 'securitySolution.rulesManagementPage.newFeaturesTour.v8.6',
};

export const RULE_DETAILS_EXECUTION_LOG_TABLE_SHOW_METRIC_COLUMNS_STORAGE_KEY =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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 { EuiText, EuiTourStep } from '@elastic/eui';
import React, { useCallback, useEffect, useState } from 'react';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../../../../../../common/constants';
import { useKibana } from '../../../../../common/lib/kibana';
import * as i18n from './translations';

export interface Props {
children: React.ReactElement;
}

export const RulesPageTourComponent: React.FC<Props> = ({ children }) => {
const tourConfig = {
currentTourStep: 1,
isTourActive: true,
tourPopoverWidth: 300,
};

const { storage } = useKibana().services;

const [tourState, setTourState] = useState(() => {
const restoredTourState = storage.get(NEW_FEATURES_TOUR_STORAGE_KEYS.RULE_MANAGEMENT_PAGE);

if (restoredTourState != null) {
return restoredTourState;
}
return tourConfig;
});

const demoTourSteps = [
{
step: 1,
title: i18n.CREATE_RULE_TOUR_TITLE,
content: <EuiText>{i18n.CREATE_RULE_TOUR_CONTENT}</EuiText>,
},
];
const finishTour = useCallback(() => {
setTourState({
...tourState,
isTourActive: false,
});
}, [tourState]);

useEffect(() => {
storage.set(NEW_FEATURES_TOUR_STORAGE_KEYS.RULE_MANAGEMENT_PAGE, tourState);
}, [tourState, storage]);

return (
<EuiTourStep
content={demoTourSteps[0].content}
isStepOpen={tourState.currentTourStep === 1 && tourState.isTourActive}
minWidth={tourState.tourPopoverWidth}
onFinish={finishTour}
step={1}
stepsTotal={demoTourSteps.length}
subtitle={tourState.tourSubtitle}
title={demoTourSteps[0].title}
anchorPosition="rightUp"
>
{children}
</EuiTourStep>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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 { i18n } from '@kbn/i18n';

export const CREATE_RULE_TOUR_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.tour.createRuleTourTitle',
{
defaultMessage: 'New security rule features are available',
}
);

export const CREATE_RULE_TOUR_CONTENT = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.tour.createRuleTourContent',
{
defaultMessage: `Alert suppression options are now available for Custom Query rules and multiple fields can be selected in New Terms rules`,
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { AllRules } from '../../components/rules_table';
import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context';

import * as i18n from '../../../../detections/pages/detection_engine/rules/translations';
import { RulesPageTourComponent } from '../../components/rules_table/alternative_tour/tour';

const RulesPageComponent: React.FC = () => {
const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState();
Expand Down Expand Up @@ -140,17 +141,19 @@ const RulesPageComponent: React.FC = () => {
{i18n.IMPORT_RULE}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<SecuritySolutionLinkButton
data-test-subj="create-new-rule"
fill
iconType="plusInCircle"
isDisabled={!hasUserCRUDPermission(canUserCRUD) || loading}
deepLinkId={SecurityPageName.rulesCreate}
>
{i18n.ADD_NEW_RULE}
</SecuritySolutionLinkButton>
</EuiFlexItem>
<RulesPageTourComponent>
<EuiFlexItem grow={false}>
<SecuritySolutionLinkButton
data-test-subj="create-new-rule"
fill
iconType="plusInCircle"
isDisabled={!hasUserCRUDPermission(canUserCRUD) || loading}
deepLinkId={SecurityPageName.rulesCreate}
>
{i18n.ADD_NEW_RULE}
</SecuritySolutionLinkButton>
</EuiFlexItem>
</RulesPageTourComponent>
</EuiFlexGroup>
</HeaderPage>
{(prePackagedRuleStatus === 'ruleNeedUpdate' ||
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -32393,8 +32393,6 @@
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.disableAllTitle": "Désactiver",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.enableAllTitle": "Activer",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDeleteRulesMessage": "Impossible de supprimer la ou les règles",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDisableRulesMessage": "Impossible de désactiver la ou les règles",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToEnableRulesMessage": "Impossible d'activer la ou les règles",
"xpack.triggersActionsUI.sections.rulesList.cancelSnooze": "Annuler la répétition",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmCallout": "Seule l'occurrence actuelle d'un calendrier sera annulée.",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmText": "Reprenez la notification lorsque des alertes sont générées comme défini dans les actions de la règle.",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -32367,8 +32367,6 @@
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.disableAllTitle": "無効にする",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.enableAllTitle": "有効にする",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDeleteRulesMessage": "ルールを削除できませんでした",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDisableRulesMessage": "ルールを無効にできませんでした",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToEnableRulesMessage": "ルールを有効にできませんでした",
"xpack.triggersActionsUI.sections.rulesList.cancelSnooze": "スヌーズをキャンセル",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmCallout": "スケジュールの最新の発生のみがキャンセルされます。",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmText": "ルールアクションの定義に従ってアラートが生成されるときに通知を再開します。",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -32404,8 +32404,6 @@
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.disableAllTitle": "禁用",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.enableAllTitle": "启用",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDeleteRulesMessage": "无法删除规则",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToDisableRulesMessage": "无法禁用规则",
"xpack.triggersActionsUI.sections.rulesList.bulkActionPopover.failedToEnableRulesMessage": "无法启用规则",
"xpack.triggersActionsUI.sections.rulesList.cancelSnooze": "取消暂停",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmCallout": "只会取消当前发生的计划。",
"xpack.triggersActionsUI.sections.rulesList.cancelSnoozeConfirmText": "根据规则操作中的定义生成告警时恢复通知。",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export function useBulkEditSelect(props: UseBulkEditSelectProps) {

return useMemo(() => {
return {
selectedIds: state.selectedIds,
selectedIds: [...state.selectedIds],
isAllSelected: state.isAllSelected,
isPageSelected,
numberOfSelectedItems,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,41 @@ import React, { useCallback, useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import type { BulkOperationError } from '@kbn/alerting-plugin/server';
import { useKibana } from '../../common/lib/kibana';
import { BulkDeleteResponse } from '../../types';
import {
getSuccessfulDeletionNotificationText,
getFailedDeletionNotificationText,
getPartialSuccessDeletionNotificationText,
getPartialSuccessEnablingNotificationText,
getPartialSuccessDisablingNotificationText,
getFailedEnablingNotificationText,
getFailedDisablingNotificationText,
getSuccessfulEnablingNotificationText,
getSuccessfulDisablingNotificationText,
SINGLE_RULE_TITLE,
MULTIPLE_RULE_TITLE,
} from '../sections/rules_list/translations';

export const useBulkDeleteResponse = ({
const actionToToastMapping = {
DELETE: {
getSuccessfulNotificationText: getSuccessfulDeletionNotificationText,
getFailedNotificationText: getFailedDeletionNotificationText,
getPartialSuccessNotificationText: getPartialSuccessDeletionNotificationText,
},
ENABLE: {
getSuccessfulNotificationText: getSuccessfulEnablingNotificationText,
getFailedNotificationText: getFailedEnablingNotificationText,
getPartialSuccessNotificationText: getPartialSuccessEnablingNotificationText,
},
DISABLE: {
getSuccessfulNotificationText: getSuccessfulDisablingNotificationText,
getFailedNotificationText: getFailedDisablingNotificationText,
getPartialSuccessNotificationText: getPartialSuccessDisablingNotificationText,
},
};

export const useBulkOperationToast = ({
onSearchPopulate,
}: {
onSearchPopulate?: (filter: string) => void;
Expand All @@ -28,26 +52,26 @@ export const useBulkDeleteResponse = ({
} = useKibana().services;

const onSearchPopulateInternal = useCallback(
(response: BulkDeleteResponse) => {
(errors: BulkOperationError[]) => {
if (!onSearchPopulate) {
return;
}
const filter = response.errors.map((error) => error.rule.name).join(',');
const filter = errors.map((error) => error.rule.name).join(',');
onSearchPopulate(filter);
},
[onSearchPopulate]
);

const renderToastErrorBody = useCallback(
(response: BulkDeleteResponse, messageType: 'warning' | 'danger') => {
(errors: BulkOperationError[], messageType: 'warning' | 'danger') => {
return (
<EuiFlexGroup justifyContent="flexEnd" gutterSize="xs">
{onSearchPopulate && (
<EuiFlexItem grow={false}>
<EuiButton
color={messageType}
size="s"
onClick={() => onSearchPopulateInternal(response)}
onClick={() => onSearchPopulateInternal(errors)}
data-test-subj="bulkDeleteResponseFilterErrors"
>
<FormattedMessage
Expand All @@ -64,16 +88,22 @@ export const useBulkDeleteResponse = ({
);

const showToast = useCallback(
(response: BulkDeleteResponse) => {
const { errors, total } = response;

({
action,
errors,
total,
}: {
action: 'DELETE' | 'ENABLE' | 'DISABLE';
errors: BulkOperationError[];
total: number;
}) => {
const numberOfSuccess = total - errors.length;
const numberOfErrors = errors.length;

// All success
if (!numberOfErrors) {
toasts.addSuccess(
getSuccessfulDeletionNotificationText(
actionToToastMapping[action].getSuccessfulNotificationText(
numberOfSuccess,
SINGLE_RULE_TITLE,
MULTIPLE_RULE_TITLE
Expand All @@ -85,25 +115,25 @@ export const useBulkDeleteResponse = ({
// All failure
if (numberOfErrors === total) {
toasts.addDanger({
title: getFailedDeletionNotificationText(
title: actionToToastMapping[action].getFailedNotificationText(
numberOfErrors,
SINGLE_RULE_TITLE,
MULTIPLE_RULE_TITLE
),
text: toMountPoint(renderToastErrorBody(response, 'danger')),
text: toMountPoint(renderToastErrorBody(errors, 'danger')),
});
return;
}

// Some failure
toasts.addWarning({
title: getPartialSuccessDeletionNotificationText(
title: actionToToastMapping[action].getPartialSuccessNotificationText(
numberOfSuccess,
numberOfErrors,
SINGLE_RULE_TITLE,
MULTIPLE_RULE_TITLE
),
text: toMountPoint(renderToastErrorBody(response, 'warning')),
text: toMountPoint(renderToastErrorBody(errors, 'warning')),
});
},
[toasts, renderToastErrorBody]
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { HttpSetup } from '@kbn/core/public';
import { KueryNode } from '@kbn/es-query';
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
import { BulkDisableResponse } from '../../../types';

export const bulkDisableRules = async ({
filter,
ids,
http,
}: {
filter?: KueryNode | null;
ids?: string[];
http: HttpSetup;
}): Promise<BulkDisableResponse> => {
try {
const body = JSON.stringify({
ids: ids?.length ? ids : undefined,
...(filter ? { filter: JSON.stringify(filter) } : {}),
});

return http.patch(`${INTERNAL_BASE_ALERTING_API_PATH}/rules/_bulk_disable`, { body });
} catch (e) {
throw new Error(`Unable to parse bulk disable params: ${e}`);
}
};
Original file line number Diff line number Diff line change
@@ -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 { HttpSetup } from '@kbn/core/public';
import { KueryNode } from '@kbn/es-query';
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
import { BulkEnableResponse } from '../../../types';

export const bulkEnableRules = async ({
filter,
ids,
http,
}: {
filter?: KueryNode | null;
ids?: string[];
http: HttpSetup;
}): Promise<BulkEnableResponse> => {
try {
const body = JSON.stringify({
ids: ids?.length ? ids : undefined,
...(filter ? { filter: JSON.stringify(filter) } : {}),
});

return http.patch(`${INTERNAL_BASE_ALERTING_API_PATH}/rules/_bulk_enable`, { body });
} catch (e) {
throw new Error(`Unable to parse bulk enable params: ${e}`);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,5 @@ export type { BulkUpdateAPIKeyProps } from './update_api_key';
export { updateAPIKey, bulkUpdateAPIKey } from './update_api_key';
export { runSoon } from './run_soon';
export { bulkDeleteRules } from './bulk_delete';
export { bulkEnableRules } from './bulk_enable';
export { bulkDisableRules } from './bulk_disable';
Loading

0 comments on commit 9f287bb

Please sign in to comment.