[Security Solution] Update button text according to status (#80389)

* update button text according to status

* remove unused translations

* fix functional test

* fixup

* fix unit test

* update unit tests

* update unit test
This commit is contained in:
Angela Chuang 2020-10-15 18:46:11 +01:00 committed by GitHub
parent 41db7d175c
commit 6b8e8a5b46
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 493 additions and 125 deletions

View file

@ -5,10 +5,12 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { waitFor } from '@testing-library/react';
import { shallow, mount, ReactWrapper } from 'enzyme';
import '../../../../common/mock/match_media';
import { PrePackagedRulesPrompt } from './load_empty_prompt';
import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api';
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
@ -23,16 +25,94 @@ jest.mock('react-router-dom', () => {
jest.mock('../../../../common/components/link_to');
jest.mock('../../../containers/detection_engine/rules/api', () => ({
getPrePackagedRulesStatus: jest.fn().mockResolvedValue({
rules_not_installed: 0,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 0,
timelines_installed: 0,
timelines_not_updated: 0,
}),
createPrepackagedRules: jest.fn(),
}));
const props = {
createPrePackagedRules: jest.fn(),
loading: false,
userHasNoPermissions: false,
'data-test-subj': 'load-prebuilt-rules',
};
describe('PrePackagedRulesPrompt', () => {
it('renders correctly', () => {
const wrapper = shallow(
<PrePackagedRulesPrompt
createPrePackagedRules={jest.fn()}
loading={false}
userHasNoPermissions={false}
/>
);
const wrapper = shallow(<PrePackagedRulesPrompt {...props} />);
expect(wrapper.find('EmptyPrompt')).toHaveLength(1);
});
});
describe('LoadPrebuiltRulesAndTemplatesButton', () => {
it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 3,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 3,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
'Load Elastic prebuilt rules and timeline templates'
);
});
});
it('renders correct button with correct text - Load Elastic prebuilt rules', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 3,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 0,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
'Load Elastic prebuilt rules'
);
});
});
it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 0,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 3,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(<PrePackagedRulesPrompt {...props} />);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual(
'Load Elastic prebuilt timeline templates'
);
});
});
});

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
import React, { memo, useCallback } from 'react';
import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { memo, useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
@ -14,6 +14,8 @@ import * as i18n from './translations';
import { LinkButton } from '../../../../common/components/links';
import { SecurityPageName } from '../../../../app/types';
import { useFormatUrl } from '../../../../common/components/link_to';
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
import { useUserData } from '../../user_info';
const EmptyPrompt = styled(EuiEmptyPrompt)`
align-self: center; /* Corrects horizontal centering in IE11 */
@ -46,24 +48,36 @@ const PrePackagedRulesPromptComponent: React.FC<PrePackagedRulesPromptProps> = (
[history]
);
const [
{ isSignalIndexExists, isAuthenticated, hasEncryptionKey, canUserCRUD, hasIndexWrite },
] = useUserData();
const { getLoadPrebuiltRulesAndTemplatesButton } = usePrePackagedRules({
canUserCRUD,
hasIndexWrite,
isSignalIndexExists,
isAuthenticated,
hasEncryptionKey,
});
const loadPrebuiltRulesAndTemplatesButton = useMemo(
() =>
getLoadPrebuiltRulesAndTemplatesButton({
isDisabled: userHasNoPermissions,
onClick: handlePreBuiltCreation,
fill: true,
'data-test-subj': 'load-prebuilt-rules',
}),
[getLoadPrebuiltRulesAndTemplatesButton, handlePreBuiltCreation, userHasNoPermissions]
);
return (
<EmptyPrompt
title={<h2>{i18n.PRE_BUILT_TITLE}</h2>}
body={<p>{i18n.PRE_BUILT_MSG}</p>}
actions={
<EuiFlexGroup justifyContent="center">
<EuiFlexItem grow={false}>
<EuiButton
fill
iconType="indexOpen"
isDisabled={userHasNoPermissions}
isLoading={loading}
onClick={handlePreBuiltCreation}
data-test-subj="load-prebuilt-rules"
>
{i18n.PRE_BUILT_ACTION}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>{loadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
<EuiFlexItem grow={false}>
<LinkButton
isDisabled={userHasNoPermissions}

View file

@ -21,13 +21,6 @@ export const PRE_BUILT_MSG = i18n.translate(
}
);
export const PRE_BUILT_ACTION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton',
{
defaultMessage: 'Load prebuilt detection rules and timeline templates',
}
);
export const CREATE_RULE_ACTION = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton',
{

View file

@ -40,3 +40,57 @@ export const TAG_FETCH_FAILURE = i18n.translate(
defaultMessage: 'Failed to fetch Tags',
}
);
export const LOAD_PREPACKAGED_RULES = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton',
{
defaultMessage: 'Load Elastic prebuilt rules',
}
);
export const LOAD_PREPACKAGED_TIMELINE_TEMPLATES = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedTimelineTemplatesButton',
{
defaultMessage: 'Load Elastic prebuilt timeline templates',
}
);
export const LOAD_PREPACKAGED_RULES_AND_TEMPLATES = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton',
{
defaultMessage: 'Load Elastic prebuilt rules and timeline templates',
}
);
export const RELOAD_MISSING_PREPACKAGED_RULES = (missingRules: number) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton',
{
values: { missingRules },
defaultMessage:
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ',
}
);
export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton',
{
values: { missingTimelines },
defaultMessage:
'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
}
);
export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = (
missingRules: number,
missingTimelines: number
) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton',
{
values: { missingRules, missingTimelines },
defaultMessage:
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
}
);

View file

@ -32,6 +32,10 @@ describe('usePrePackagedRules', () => {
await waitForNextUpdate();
expect(result.current).toEqual({
getLoadPrebuiltRulesAndTemplatesButton:
result.current.getLoadPrebuiltRulesAndTemplatesButton,
getReloadPrebuiltRulesAndTemplatesButton:
result.current.getReloadPrebuiltRulesAndTemplatesButton,
createPrePackagedRules: null,
loading: true,
loadingCreatePrePackagedRules: false,
@ -63,6 +67,10 @@ describe('usePrePackagedRules', () => {
await waitForNextUpdate();
expect(result.current).toEqual({
getLoadPrebuiltRulesAndTemplatesButton:
result.current.getLoadPrebuiltRulesAndTemplatesButton,
getReloadPrebuiltRulesAndTemplatesButton:
result.current.getReloadPrebuiltRulesAndTemplatesButton,
createPrePackagedRules: result.current.createPrePackagedRules,
loading: false,
loadingCreatePrePackagedRules: false,
@ -100,6 +108,10 @@ describe('usePrePackagedRules', () => {
expect(resp).toEqual(true);
expect(spyOnCreatePrepackagedRules).toHaveBeenCalled();
expect(result.current).toEqual({
getLoadPrebuiltRulesAndTemplatesButton:
result.current.getLoadPrebuiltRulesAndTemplatesButton,
getReloadPrebuiltRulesAndTemplatesButton:
result.current.getReloadPrebuiltRulesAndTemplatesButton,
createPrePackagedRules: result.current.createPrePackagedRules,
loading: false,
loadingCreatePrePackagedRules: false,

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { useEffect, useState } from 'react';
import React, { useCallback, useMemo, useState, useEffect } from 'react';
import { EuiButton } from '@elastic/eui';
import {
errorToToaster,
@ -14,6 +15,11 @@ import {
import { getPrePackagedRulesStatus, createPrepackagedRules } from './api';
import * as i18n from './translations';
import {
getPrePackagedRuleStatus,
getPrePackagedTimelineStatus,
} from '../../../pages/detection_engine/rules/helpers';
type Func = () => void;
export type CreatePreBuiltRules = () => Promise<boolean>;
@ -23,6 +29,23 @@ interface ReturnPrePackagedTimelines {
timelinesNotUpdated: number | null;
}
type GetLoadPrebuiltRulesAndTemplatesButton = (args: {
isDisabled: boolean;
onClick: () => void;
fill?: boolean;
'data-test-subj'?: string;
}) => React.ReactNode | null;
type GetReloadPrebuiltRulesAndTemplatesButton = ({
isDisabled,
onClick,
fill,
}: {
isDisabled: boolean;
onClick: () => void;
fill?: boolean;
}) => React.ReactNode | null;
interface ReturnPrePackagedRules {
createPrePackagedRules: null | CreatePreBuiltRules;
loading: boolean;
@ -32,6 +55,8 @@ interface ReturnPrePackagedRules {
rulesInstalled: number | null;
rulesNotInstalled: number | null;
rulesNotUpdated: number | null;
getLoadPrebuiltRulesAndTemplatesButton: GetLoadPrebuiltRulesAndTemplatesButton;
getReloadPrebuiltRulesAndTemplatesButton: GetReloadPrebuiltRulesAndTemplatesButton;
}
export type ReturnPrePackagedRulesAndTimelines = ReturnPrePackagedRules &
@ -89,7 +114,6 @@ export const usePrePackagedRules = ({
const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false);
const [loading, setLoading] = useState(true);
const [, dispatchToaster] = useStateToaster();
useEffect(() => {
let isSubscribed = true;
const abortCtrl = new AbortController();
@ -100,7 +124,6 @@ export const usePrePackagedRules = ({
const prePackagedRuleStatusResponse = await getPrePackagedRulesStatus({
signal: abortCtrl.signal,
});
if (isSubscribed) {
setPrepackagedDataStatus({
createPrePackagedRules: createElasticRules,
@ -225,9 +248,108 @@ export const usePrePackagedRules = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [canUserCRUD, hasIndexWrite, isAuthenticated, hasEncryptionKey, isSignalIndexExists]);
const prePackagedRuleStatus = useMemo(
() =>
getPrePackagedRuleStatus(
prepackagedDataStatus.rulesInstalled,
prepackagedDataStatus.rulesNotInstalled,
prepackagedDataStatus.rulesNotUpdated
),
[
prepackagedDataStatus.rulesInstalled,
prepackagedDataStatus.rulesNotInstalled,
prepackagedDataStatus.rulesNotUpdated,
]
);
const prePackagedTimelineStatus = useMemo(
() =>
getPrePackagedTimelineStatus(
prepackagedDataStatus.timelinesInstalled,
prepackagedDataStatus.timelinesNotInstalled,
prepackagedDataStatus.timelinesNotUpdated
),
[
prepackagedDataStatus.timelinesInstalled,
prepackagedDataStatus.timelinesNotInstalled,
prepackagedDataStatus.timelinesNotUpdated,
]
);
const getLoadPrebuiltRulesAndTemplatesButton = useCallback(
({ isDisabled, onClick, fill, 'data-test-subj': dataTestSubj = 'loadPrebuiltRulesBtn' }) => {
return prePackagedRuleStatus === 'ruleNotInstalled' ||
prePackagedTimelineStatus === 'timelinesNotInstalled' ? (
<EuiButton
fill={fill}
iconType="indexOpen"
isLoading={loadingCreatePrePackagedRules}
isDisabled={isDisabled}
onClick={onClick}
data-test-subj={dataTestSubj}
>
{prePackagedRuleStatus === 'ruleNotInstalled' &&
prePackagedTimelineStatus === 'timelinesNotInstalled' &&
i18n.LOAD_PREPACKAGED_RULES_AND_TEMPLATES}
{prePackagedRuleStatus === 'ruleNotInstalled' &&
prePackagedTimelineStatus !== 'timelinesNotInstalled' &&
i18n.LOAD_PREPACKAGED_RULES}
{prePackagedRuleStatus !== 'ruleNotInstalled' &&
prePackagedTimelineStatus === 'timelinesNotInstalled' &&
i18n.LOAD_PREPACKAGED_TIMELINE_TEMPLATES}
</EuiButton>
) : null;
},
[loadingCreatePrePackagedRules, prePackagedRuleStatus, prePackagedTimelineStatus]
);
const getMissingRulesOrTimelinesButtonTitle = useCallback(
(missingRules: number, missingTimelines: number) => {
if (missingRules > 0 && missingTimelines === 0)
return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules);
else if (missingRules === 0 && missingTimelines > 0)
return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines);
else if (missingRules > 0 && missingTimelines > 0)
return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines);
},
[]
);
const getReloadPrebuiltRulesAndTemplatesButton = useCallback(
({ isDisabled, onClick, fill = false }) => {
return prePackagedRuleStatus === 'someRuleUninstall' ||
prePackagedTimelineStatus === 'someTimelineUninstall' ? (
<EuiButton
fill={fill}
iconType="plusInCircle"
isLoading={loadingCreatePrePackagedRules}
isDisabled={isDisabled}
onClick={onClick}
data-test-subj="reloadPrebuiltRulesBtn"
>
{getMissingRulesOrTimelinesButtonTitle(
prepackagedDataStatus.rulesNotInstalled ?? 0,
prepackagedDataStatus.timelinesNotInstalled ?? 0
)}
</EuiButton>
) : null;
},
[
getMissingRulesOrTimelinesButtonTitle,
loadingCreatePrePackagedRules,
prePackagedRuleStatus,
prePackagedTimelineStatus,
prepackagedDataStatus.rulesNotInstalled,
prepackagedDataStatus.timelinesNotInstalled,
]
);
return {
loading,
loadingCreatePrePackagedRules,
...prepackagedDataStatus,
getLoadPrebuiltRulesAndTemplatesButton,
getReloadPrebuiltRulesAndTemplatesButton,
};
};

View file

@ -5,13 +5,14 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { shallow, mount, ReactWrapper } from 'enzyme';
import '../../../../common/mock/match_media';
import { RulesPage } from './index';
import { useUserData } from '../../../components/user_info';
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
import { waitFor } from '@testing-library/react';
import { TestProviders } from '../../../../common/mock';
import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api';
jest.mock('react-router-dom', () => {
const original = jest.requireActual('react-router-dom');
@ -26,16 +27,164 @@ jest.mock('react-router-dom', () => {
jest.mock('../../../containers/detection_engine/lists/use_lists_config');
jest.mock('../../../../common/components/link_to');
jest.mock('../../../components/user_info');
jest.mock('../../../containers/detection_engine/rules');
jest.mock('../../../../common/components/toasters', () => {
const actual = jest.requireActual('../../../../common/components/toasters');
return {
...actual,
errorToToaster: jest.fn(),
useStateToaster: jest.fn().mockReturnValue([jest.fn(), jest.fn()]),
displaySuccessToast: jest.fn(),
};
});
jest.mock('../../../containers/detection_engine/rules/api', () => ({
getPrePackagedRulesStatus: jest.fn().mockResolvedValue({
rules_not_installed: 0,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 0,
timelines_installed: 0,
timelines_not_updated: 0,
}),
createPrepackagedRules: jest.fn(),
}));
jest.mock('../../../../common/lib/kibana', () => {
return {
useToast: jest.fn(),
useHttp: jest.fn(),
};
});
jest.mock('../../../components/value_lists_management_modal', () => {
return {
ValueListsModal: jest.fn().mockReturnValue(<div />),
};
});
jest.mock('./all', () => {
return {
AllRules: jest.fn().mockReturnValue(<div />),
};
});
jest.mock('../../../../common/utils/route/spy_routes', () => {
return {
SpyRoute: jest.fn().mockReturnValue(<div />),
};
});
jest.mock('../../../components/rules/pre_packaged_rules/update_callout', () => {
return {
UpdatePrePackagedRulesCallOut: jest.fn().mockReturnValue(<div />),
};
});
describe('RulesPage', () => {
beforeAll(() => {
(useUserData as jest.Mock).mockReturnValue([{}]);
(usePrePackagedRules as jest.Mock).mockReturnValue({});
});
it('renders correctly', () => {
const wrapper = shallow(<RulesPage />);
expect(wrapper.find('AllRules')).toHaveLength(1);
it('renders AllRules', () => {
const wrapper = shallow(<RulesPage />);
expect(wrapper.find('[data-test-subj="all-rules"]').exists()).toEqual(true);
});
it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 3,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 3,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(
<TestProviders>
<RulesPage />
</TestProviders>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
'Load Elastic prebuilt rules and timeline templates'
);
});
});
it('renders correct button with correct text - Load Elastic prebuilt rules', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 3,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 0,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(
<TestProviders>
<RulesPage />
</TestProviders>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
'Load Elastic prebuilt rules'
);
});
});
it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 0,
rules_installed: 0,
rules_not_updated: 0,
timelines_not_installed: 3,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(
<TestProviders>
<RulesPage />
</TestProviders>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true);
expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual(
'Load Elastic prebuilt timeline templates'
);
});
});
it('renders a callout - Update Elastic prebuilt rules', async () => {
(getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({
rules_not_installed: 2,
rules_installed: 1,
rules_not_updated: 1,
timelines_not_installed: 0,
timelines_installed: 0,
timelines_not_updated: 0,
});
const wrapper: ReactWrapper = mount(
<TestProviders>
<RulesPage />
</TestProviders>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('[data-test-subj="update-callout-button"]').exists()).toEqual(true);
});
});
});

View file

@ -5,7 +5,7 @@
*/
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import React, { useCallback, useRef, useState } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { usePrePackagedRules, importRules } from '../../../containers/detection_engine/rules';
@ -70,6 +70,8 @@ const RulesPageComponent: React.FC = () => {
timelinesInstalled,
timelinesNotInstalled,
timelinesNotUpdated,
getLoadPrebuiltRulesAndTemplatesButton,
getReloadPrebuiltRulesAndTemplatesButton,
} = usePrePackagedRules({
canUserCRUD,
hasIndexWrite,
@ -113,18 +115,6 @@ const RulesPageComponent: React.FC = () => {
refreshRulesData.current = refreshRule;
}, []);
const getMissingRulesOrTimelinesButtonTitle = useCallback(
(missingRules: number, missingTimelines: number) => {
if (missingRules > 0 && missingTimelines === 0)
return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules);
else if (missingRules === 0 && missingTimelines > 0)
return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines);
else if (missingRules > 0 && missingTimelines > 0)
return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines);
},
[]
);
const goToNewRule = useCallback(
(ev) => {
ev.preventDefault();
@ -133,6 +123,24 @@ const RulesPageComponent: React.FC = () => {
[history]
);
const loadPrebuiltRulesAndTemplatesButton = useMemo(
() =>
getLoadPrebuiltRulesAndTemplatesButton({
isDisabled: userHasNoPermissions(canUserCRUD) || loading,
onClick: handleCreatePrePackagedRules,
}),
[canUserCRUD, getLoadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading]
);
const reloadPrebuiltRulesAndTemplatesButton = useMemo(
() =>
getReloadPrebuiltRulesAndTemplatesButton({
isDisabled: userHasNoPermissions(canUserCRUD) || loading,
onClick: handleCreatePrePackagedRules,
}),
[canUserCRUD, getReloadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading]
);
if (
redirectToDetections(
isSignalIndexExists,
@ -177,35 +185,11 @@ const RulesPageComponent: React.FC = () => {
title={i18n.PAGE_TITLE}
>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap={true}>
{(prePackagedRuleStatus === 'ruleNotInstalled' ||
prePackagedTimelineStatus === 'timelinesNotInstalled') && (
<EuiFlexItem grow={false}>
<EuiButton
iconType="indexOpen"
isLoading={loadingCreatePrePackagedRules}
isDisabled={userHasNoPermissions(canUserCRUD) || loading}
onClick={handleCreatePrePackagedRules}
>
{i18n.LOAD_PREPACKAGED_RULES}
</EuiButton>
</EuiFlexItem>
{loadPrebuiltRulesAndTemplatesButton && (
<EuiFlexItem grow={false}>{loadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
)}
{(prePackagedRuleStatus === 'someRuleUninstall' ||
prePackagedTimelineStatus === 'someTimelineUninstall') && (
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="reloadPrebuiltRulesBtn"
iconType="plusInCircle"
isLoading={loadingCreatePrePackagedRules}
isDisabled={userHasNoPermissions(canUserCRUD) || loading}
onClick={handleCreatePrePackagedRules}
>
{getMissingRulesOrTimelinesButtonTitle(
rulesNotInstalled ?? 0,
timelinesNotInstalled ?? 0
)}
</EuiButton>
</EuiFlexItem>
{reloadPrebuiltRulesAndTemplatesButton && (
<EuiFlexItem grow={false}>{reloadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiToolTip position="top" content={i18n.UPLOAD_VALUE_LISTS_TOOLTIP}>
@ -247,6 +231,7 @@ const RulesPageComponent: React.FC = () => {
{(prePackagedRuleStatus === 'ruleNeedUpdate' ||
prePackagedTimelineStatus === 'timelineNeedUpdate') && (
<UpdatePrePackagedRulesCallOut
data-test-subj="update-callout-button"
loading={loadingCreatePrePackagedRules}
numberOfUpdatedRules={rulesNotUpdated ?? 0}
numberOfUpdatedTimelines={timelinesNotUpdated ?? 0}
@ -255,6 +240,7 @@ const RulesPageComponent: React.FC = () => {
)}
<AllRules
createPrePackagedRules={createPrePackagedRules}
data-test-subj="all-rules"
loading={loading || prePackagedRuleLoading}
loadingCreatePrePackagedRules={loadingCreatePrePackagedRules}
hasNoPermissions={userHasNoPermissions(canUserCRUD)}

View file

@ -494,46 +494,6 @@ export const DELETE = i18n.translate(
}
);
export const LOAD_PREPACKAGED_RULES = i18n.translate(
'xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton',
{
defaultMessage: 'Load Elastic prebuilt rules and timeline templates',
}
);
export const RELOAD_MISSING_PREPACKAGED_RULES = (missingRules: number) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton',
{
values: { missingRules },
defaultMessage:
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ',
}
);
export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton',
{
values: { missingTimelines },
defaultMessage:
'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
}
);
export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = (
missingRules: number,
missingTimelines: number
) =>
i18n.translate(
'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton',
{
values: { missingRules, missingTimelines },
defaultMessage:
'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ',
}
);
export const IMPORT_RULE_BTN_TITLE = i18n.translate(
'xpack.securitySolution.detectionEngine.components.importRuleModal.importRuleTitle',
{

View file

@ -15574,13 +15574,12 @@
"xpack.securitySolution.detectionEngine.rules.deleteDescription": "削除",
"xpack.securitySolution.detectionEngine.rules.editPageTitle": "編集",
"xpack.securitySolution.detectionEngine.rules.importRuleTitle": "ルールのインポート...",
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む",
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む",
"xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "オプション",
"xpack.securitySolution.detectionEngine.rules.pageTitle": "検出ルール",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "独自のルールの作成",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elasticセキュリティには、バックグラウンドで実行され、条件が合うとアラートを作成する事前構築済み検出ルールがあります。デフォルトでは、Elastic Endpoint Securityルールを除くすべての事前構築済みルールが無効になっています。有効にする追加のルールを選択することができます。",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "Elastic事前構築済み検出ルールを読み込む",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "事前構築済み検出ルールおよびタイムラインテンプレートを読み込む",
"xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "リリースノート",
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "{missingRules} Elastic事前構築済み{missingRules, plural, =1 {ルール} other {ルール}}と{missingTimelines} Elastic事前構築済み{missingTimelines, plural, =1 {タイムライン} other {タイムライン}}をインストール ",
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "{missingRules} Elasticの事前構築済みの{missingRules, plural, =1 {個のルール} other {個のルール}}をインストール ",

View file

@ -15583,13 +15583,12 @@
"xpack.securitySolution.detectionEngine.rules.deleteDescription": "删除",
"xpack.securitySolution.detectionEngine.rules.editPageTitle": "编辑",
"xpack.securitySolution.detectionEngine.rules.importRuleTitle": "导入规则……",
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "加载 Elastic 预构建规则和时间线模板",
"xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "加载 Elastic 预构建规则和时间线模板",
"xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "可选",
"xpack.securitySolution.detectionEngine.rules.pageTitle": "检测规则",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "创建自己的规则",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elastic Security 附带预置检测规则,这些规则在后台运行,并在条件满足时创建告警。默认情况下,除 Elastic Endpoint Security 规则外,所有预置规则都处于禁用状态。您可以选择其他要激活的规则。",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "加载 Elastic 预构建检测规则",
"xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "加载预置检测规则和时间线模板",
"xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "发行说明",
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}}以及 {missingTimelines} 个 Elastic 预构建{missingTimelines, plural, =1 {时间线} other {时间线}} ",
"xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}} ",