mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Security solution] [Endpoint] Event filters license downgrade experience (#121738)
* Hide assignment section and show a banner when license has been downgraded. Also fixes some edge cases on trusted apps * Adds unit tests for licence downgrade experience * Adds more tests * Adds tests and move code outside form to be in flyout component * Fixes unit test and added api mocks for event filter calls * Updates API docs * Revert Trusted Apps changes because they need to be included in a separated pr Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
7b0ab30de3
commit
9fe39bad91
9 changed files with 244 additions and 28 deletions
|
@ -213,6 +213,7 @@ readonly links: {
|
|||
};
|
||||
readonly securitySolution: {
|
||||
readonly trustedApps: string;
|
||||
readonly eventFilters: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -311,6 +311,7 @@ export class DocLinksService {
|
|||
},
|
||||
securitySolution: {
|
||||
trustedApps: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/trusted-apps-ov.html`,
|
||||
eventFilters: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/event-filters.html`,
|
||||
},
|
||||
query: {
|
||||
eql: `${ELASTICSEARCH_DOCS}eql.html`,
|
||||
|
@ -801,6 +802,7 @@ export interface DocLinksStart {
|
|||
};
|
||||
readonly securitySolution: {
|
||||
readonly trustedApps: string;
|
||||
readonly eventFilters: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
|
@ -696,6 +696,7 @@ export interface DocLinksStart {
|
|||
};
|
||||
readonly securitySolution: {
|
||||
readonly trustedApps: string;
|
||||
readonly eventFilters: string;
|
||||
};
|
||||
readonly query: {
|
||||
readonly eql: string;
|
||||
|
|
|
@ -104,6 +104,9 @@ export const createdEventFilterEntryMock = (): ExceptionListItemSchema => ({
|
|||
export type EventFiltersListQueryHttpMockProviders = ResponseProvidersInterface<{
|
||||
eventFiltersList: () => FoundExceptionListItemSchema;
|
||||
eventFiltersCreateList: () => ExceptionListItemSchema;
|
||||
eventFiltersGetOne: () => ExceptionListItemSchema;
|
||||
eventFiltersCreateOne: () => ExceptionListItemSchema;
|
||||
eventFiltersUpdateOne: () => ExceptionListItemSchema;
|
||||
}>;
|
||||
|
||||
export const esResponseData = () => ({
|
||||
|
@ -228,4 +231,28 @@ export const eventFiltersListQueryHttpMock =
|
|||
return getFoundExceptionListItemSchemaMock();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'eventFiltersGetOne',
|
||||
method: 'get',
|
||||
path: `${EXCEPTION_LIST_ITEM_URL}`,
|
||||
handler: (): ExceptionListItemSchema => {
|
||||
return getExceptionListItemSchemaMock();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'eventFiltersCreateOne',
|
||||
method: 'post',
|
||||
path: `${EXCEPTION_LIST_ITEM_URL}`,
|
||||
handler: (): ExceptionListItemSchema => {
|
||||
return getExceptionListItemSchemaMock();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'eventFiltersUpdateOne',
|
||||
method: 'put',
|
||||
path: `${EXCEPTION_LIST_ITEM_URL}`,
|
||||
handler: (): ExceptionListItemSchema => {
|
||||
return getExceptionListItemSchemaMock();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -19,15 +19,15 @@ import type {
|
|||
CreateExceptionListItemSchema,
|
||||
ExceptionListItemSchema,
|
||||
} from '@kbn/securitysolution-io-ts-list-types';
|
||||
import { EventFiltersHttpService } from '../../../service';
|
||||
import { createdEventFilterEntryMock, ecsEventMock, esResponseData } from '../../../test_utils';
|
||||
import { ecsEventMock, esResponseData, eventFiltersListQueryHttpMock } from '../../../test_utils';
|
||||
import { getFormEntryState, isUninitialisedForm } from '../../../store/selector';
|
||||
import { EventFiltersListPageState } from '../../../types';
|
||||
import { useKibana } from '../../../../../../common/lib/kibana';
|
||||
import { licenseService } from '../../../../../../common/hooks/use_license';
|
||||
import { getExceptionListItemSchemaMock } from '../../../../../../../../lists/common/schemas/response/exception_list_item_schema.mock';
|
||||
|
||||
jest.mock('../../../../../../common/lib/kibana');
|
||||
jest.mock('../form');
|
||||
jest.mock('../../../service');
|
||||
jest.mock('../../../../../services/policies/policies');
|
||||
|
||||
jest.mock('../../hooks', () => {
|
||||
|
@ -40,6 +40,18 @@ jest.mock('../../hooks', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../../../common/hooks/use_license', () => {
|
||||
const licenseServiceInstance = {
|
||||
isPlatinumPlus: jest.fn(),
|
||||
};
|
||||
return {
|
||||
licenseService: licenseServiceInstance,
|
||||
useLicense: () => {
|
||||
return licenseServiceInstance;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
(sendGetEndpointSpecificPackagePolicies as jest.Mock).mockImplementation(
|
||||
sendGetEndpointSpecificPackagePoliciesMock
|
||||
);
|
||||
|
@ -52,29 +64,31 @@ let render: (
|
|||
) => ReturnType<AppContextTestRender['render']>;
|
||||
const act = reactTestingLibrary.act;
|
||||
let onCancelMock: jest.Mock;
|
||||
const EventFiltersHttpServiceMock = EventFiltersHttpService as jest.Mock;
|
||||
let getState: () => EventFiltersListPageState;
|
||||
let mockedApi: ReturnType<typeof eventFiltersListQueryHttpMock>;
|
||||
|
||||
describe('Event filter flyout', () => {
|
||||
beforeAll(() => {
|
||||
EventFiltersHttpServiceMock.mockImplementation(() => {
|
||||
return {
|
||||
getOne: () => createdEventFilterEntryMock(),
|
||||
addEventFilters: () => createdEventFilterEntryMock(),
|
||||
updateOne: () => createdEventFilterEntryMock(),
|
||||
};
|
||||
});
|
||||
});
|
||||
beforeEach(() => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true);
|
||||
mockedContext = createAppRootMockRenderer();
|
||||
waitForAction = mockedContext.middlewareSpy.waitForAction;
|
||||
onCancelMock = jest.fn();
|
||||
getState = () => mockedContext.store.getState().management.eventFilters;
|
||||
render = (props) =>
|
||||
mockedContext.render(<EventFiltersFlyout {...props} onCancel={onCancelMock} />);
|
||||
mockedApi = eventFiltersListQueryHttpMock(mockedContext.coreStart.http);
|
||||
|
||||
render = (props) => {
|
||||
return mockedContext.render(<EventFiltersFlyout {...props} onCancel={onCancelMock} />);
|
||||
};
|
||||
|
||||
(useKibana as jest.Mock).mockReturnValue({
|
||||
services: {
|
||||
docLinks: {
|
||||
links: {
|
||||
securitySolution: {
|
||||
eventFilters: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
http: {},
|
||||
data: {
|
||||
search: {
|
||||
|
@ -227,6 +241,46 @@ describe('Event filter flyout', () => {
|
|||
});
|
||||
|
||||
expect(getFormEntryState(getState())).not.toBeUndefined();
|
||||
expect(getFormEntryState(getState())?.item_id).toBe(createdEventFilterEntryMock().item_id);
|
||||
expect(getFormEntryState(getState())?.item_id).toBe(
|
||||
mockedApi.responseProvider.eventFiltersGetOne.getMockImplementation()!().item_id
|
||||
);
|
||||
});
|
||||
|
||||
it('should not display banner when platinum license', async () => {
|
||||
await act(async () => {
|
||||
component = render({ id: 'fakeId', type: 'edit' });
|
||||
await waitForAction('eventFiltersInitFromId');
|
||||
});
|
||||
|
||||
expect(component.queryByTestId('expired-license-callout')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not display banner when under platinum license and create mode', async () => {
|
||||
component = render();
|
||||
expect(component.queryByTestId('expired-license-callout')).toBeNull();
|
||||
});
|
||||
|
||||
it('should not display banner when under platinum license and edit mode with global assignment', async () => {
|
||||
mockedApi.responseProvider.eventFiltersGetOne.mockReturnValue({
|
||||
...getExceptionListItemSchemaMock(),
|
||||
tags: ['policy:all'],
|
||||
});
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
await act(async () => {
|
||||
component = render({ id: 'fakeId', type: 'edit' });
|
||||
await waitForAction('eventFiltersInitFromId');
|
||||
});
|
||||
|
||||
expect(component.queryByTestId('expired-license-callout')).toBeNull();
|
||||
});
|
||||
|
||||
it('should display banner when under platinum license and edit mode with by policy assignment', async () => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
await act(async () => {
|
||||
component = render({ id: 'fakeId', type: 'edit' });
|
||||
await waitForAction('eventFiltersInitFromId');
|
||||
});
|
||||
|
||||
expect(component.queryByTestId('expired-license-callout')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import { useDispatch } from 'react-redux';
|
|||
import { Dispatch } from 'redux';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
|
@ -21,11 +22,14 @@ import {
|
|||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiTextColor,
|
||||
EuiCallOut,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
import { AppAction } from '../../../../../../common/store/actions';
|
||||
import { EventFiltersForm } from '../form';
|
||||
import { useEventFiltersSelector, useEventFiltersNotification } from '../../hooks';
|
||||
import {
|
||||
getFormEntryStateMutable,
|
||||
getFormHasError,
|
||||
isCreationInProgress,
|
||||
isCreationSuccessful,
|
||||
|
@ -35,6 +39,8 @@ import { Ecs } from '../../../../../../../common/ecs';
|
|||
import { useKibana, useToasts } from '../../../../../../common/lib/kibana';
|
||||
import { useGetEndpointSpecificPolicies } from '../../../../../services/policies/hooks';
|
||||
import { getLoadPoliciesError } from '../../../../../common/translations';
|
||||
import { useLicense } from '../../../../../../common/hooks/use_license';
|
||||
import { isGlobalPolicyEffected } from '../../../../../components/effected_policy_select/utils';
|
||||
|
||||
export interface EventFiltersFlyoutProps {
|
||||
type?: 'create' | 'edit';
|
||||
|
@ -52,8 +58,10 @@ export const EventFiltersFlyout: React.FC<EventFiltersFlyoutProps> = memo(
|
|||
const formHasError = useEventFiltersSelector(getFormHasError);
|
||||
const creationInProgress = useEventFiltersSelector(isCreationInProgress);
|
||||
const creationSuccessful = useEventFiltersSelector(isCreationSuccessful);
|
||||
const exception = useEventFiltersSelector(getFormEntryStateMutable);
|
||||
const {
|
||||
data: { search },
|
||||
docLinks,
|
||||
} = useKibana().services;
|
||||
|
||||
// load the list of policies>
|
||||
|
@ -63,6 +71,20 @@ export const EventFiltersFlyout: React.FC<EventFiltersFlyoutProps> = memo(
|
|||
},
|
||||
});
|
||||
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const isEditMode = useMemo(() => type === 'edit' && !!id, [type, id]);
|
||||
const [wasByPolicy, setWasByPolicy] = useState<boolean | undefined>(undefined);
|
||||
|
||||
const showExpiredLicenseBanner = useMemo(() => {
|
||||
return !isPlatinumPlus && isEditMode && wasByPolicy;
|
||||
}, [isPlatinumPlus, isEditMode, wasByPolicy]);
|
||||
|
||||
useEffect(() => {
|
||||
if (exception && wasByPolicy === undefined) {
|
||||
setWasByPolicy(!isGlobalPolicyEffected(exception?.tags));
|
||||
}
|
||||
}, [exception, wasByPolicy]);
|
||||
|
||||
useEffect(() => {
|
||||
if (creationSuccessful) {
|
||||
onCancel();
|
||||
|
@ -219,6 +241,28 @@ export const EventFiltersFlyout: React.FC<EventFiltersFlyoutProps> = memo(
|
|||
) : null}
|
||||
</EuiFlyoutHeader>
|
||||
|
||||
{showExpiredLicenseBanner && (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.securitySolution.eventFilters.expiredLicenseTitle', {
|
||||
defaultMessage: 'Expired License',
|
||||
})}
|
||||
color="warning"
|
||||
iconType="help"
|
||||
data-test-subj="expired-license-callout"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.eventFilters.expiredLicenseMessage"
|
||||
defaultMessage="Your Kibana license has been downgraded. Future policy configurations will now be globally assigned to all policies. For more information, see our "
|
||||
/>
|
||||
<EuiLink target="_blank" href={`${docLinks.links.securitySolution.eventFilters}`}>
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.eventFilters.docsLink"
|
||||
defaultMessage="Event Filters documentation."
|
||||
/>
|
||||
</EuiLink>
|
||||
</EuiCallOut>
|
||||
)}
|
||||
|
||||
<EuiFlyoutBody>
|
||||
<EventFiltersForm
|
||||
allowSelectOs={!data}
|
||||
|
|
|
@ -14,6 +14,7 @@ import { useFetchIndex } from '../../../../../../common/containers/source';
|
|||
import { ecsEventMock } from '../../../test_utils';
|
||||
import { NAME_ERROR, NAME_PLACEHOLDER } from './translations';
|
||||
import { useCurrentUser, useKibana } from '../../../../../../common/lib/kibana';
|
||||
import { licenseService } from '../../../../../../common/hooks/use_license';
|
||||
import {
|
||||
AppContextTestRender,
|
||||
createAppRootMockRenderer,
|
||||
|
@ -22,9 +23,21 @@ import { EventFiltersListPageState } from '../../../types';
|
|||
import { sendGetEndpointSpecificPackagePoliciesMock } from '../../../../../services/policies/test_mock_utilts';
|
||||
import { GetPolicyListResponse } from '../../../../policy/types';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
|
||||
|
||||
jest.mock('../../../../../../common/lib/kibana');
|
||||
jest.mock('../../../../../../common/containers/source');
|
||||
jest.mock('../../../../../../common/hooks/use_license', () => {
|
||||
const licenseServiceInstance = {
|
||||
isPlatinumPlus: jest.fn(),
|
||||
};
|
||||
return {
|
||||
licenseService: licenseServiceInstance,
|
||||
useLicense: () => {
|
||||
return licenseServiceInstance;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
describe('Event filter form', () => {
|
||||
let component: RenderResult;
|
||||
|
@ -32,11 +45,14 @@ describe('Event filter form', () => {
|
|||
let render: (
|
||||
props?: Partial<React.ComponentProps<typeof EventFiltersForm>>
|
||||
) => ReturnType<AppContextTestRender['render']>;
|
||||
let renderWithData: () => Promise<ReturnType<AppContextTestRender['render']>>;
|
||||
let renderWithData: (
|
||||
customEventFilterProps?: Partial<ExceptionListItemSchema>
|
||||
) => Promise<ReturnType<AppContextTestRender['render']>>;
|
||||
let getState: () => EventFiltersListPageState;
|
||||
let policiesRequest: GetPolicyListResponse;
|
||||
|
||||
beforeEach(async () => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(true);
|
||||
mockedContext = createAppRootMockRenderer();
|
||||
policiesRequest = await sendGetEndpointSpecificPackagePoliciesMock();
|
||||
getState = () => mockedContext.store.getState().management.eventFilters;
|
||||
|
@ -44,13 +60,14 @@ describe('Event filter form', () => {
|
|||
mockedContext.render(
|
||||
<EventFiltersForm policies={policiesRequest.items} arePoliciesLoading={false} {...props} />
|
||||
);
|
||||
renderWithData = async () => {
|
||||
renderWithData = async (customEventFilterProps = {}) => {
|
||||
const renderResult = render();
|
||||
const entry = getInitialExceptionFromEvent(ecsEventMock());
|
||||
|
||||
act(() => {
|
||||
mockedContext.store.dispatch({
|
||||
type: 'eventFiltersInitForm',
|
||||
payload: { entry },
|
||||
payload: { entry: { ...entry, ...customEventFilterProps } },
|
||||
});
|
||||
});
|
||||
await waitFor(() => {
|
||||
|
@ -208,4 +225,34 @@ describe('Event filter form', () => {
|
|||
// on change called with the previous policy
|
||||
expect(getState().form.entry?.tags).toEqual([`policy:${policyId}`]);
|
||||
});
|
||||
|
||||
it('should hide assignment section when no license', async () => {
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
component = await renderWithData();
|
||||
expect(component.queryByTestId('perPolicy')).toBeNull();
|
||||
});
|
||||
|
||||
it('should hide assignment section when create mode and no license even with by policy', async () => {
|
||||
const policyId = policiesRequest.items[0].id;
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
component = await renderWithData({ tags: [`policy:${policyId}`] });
|
||||
expect(component.queryByTestId('perPolicy')).toBeNull();
|
||||
});
|
||||
|
||||
it('should show disabled assignment section when edit mode and no license with by policy', async () => {
|
||||
const policyId = policiesRequest.items[0].id;
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
component = await renderWithData({ tags: [`policy:${policyId}`], item_id: '1' });
|
||||
expect(component.queryByTestId('perPolicy')).not.toBeNull();
|
||||
expect(component.getByTestId(`policy-${policyId}`).getAttribute('aria-disabled')).toBe('true');
|
||||
});
|
||||
|
||||
it('should change from by policy to global when edit mode and no license with by policy', async () => {
|
||||
const policyId = policiesRequest.items[0].id;
|
||||
(licenseService.isPlatinumPlus as jest.Mock).mockReturnValue(false);
|
||||
component = await renderWithData({ tags: [`policy:${policyId}`], item_id: '1' });
|
||||
userEvent.click(component.getByTestId('globalPolicy'));
|
||||
expect(component.queryByTestId('effectedPolicies-select-policiesSelectable')).toBeFalsy();
|
||||
expect(getState().form.entry?.tags).toEqual([`policy:all`]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
import React, { memo, useMemo, useCallback, useState, useEffect } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import {
|
||||
EuiFieldText,
|
||||
EuiSpacer,
|
||||
|
@ -49,6 +51,7 @@ import {
|
|||
getEffectedPolicySelectionByTags,
|
||||
isGlobalPolicyEffected,
|
||||
} from '../../../../../components/effected_policy_select/utils';
|
||||
import { useLicense } from '../../../../../../common/hooks/use_license';
|
||||
|
||||
const OPERATING_SYSTEMS: readonly OperatingSystem[] = [
|
||||
OperatingSystem.MAC,
|
||||
|
@ -70,6 +73,8 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
const hasNameError = useEventFiltersSelector(getHasNameError);
|
||||
const newComment = useEventFiltersSelector(getNewComment);
|
||||
const [hasBeenInputNameVisited, setHasBeenInputNameVisited] = useState(false);
|
||||
const isPlatinumPlus = useLicense().isPlatinumPlus();
|
||||
const [hasFormChanged, setHasFormChanged] = useState(false);
|
||||
|
||||
// This value has to be memoized to avoid infinite useEffect loop on useFetchIndex
|
||||
const indexNames = useMemo(() => ['logs-endpoint.events.*'], []);
|
||||
|
@ -80,6 +85,17 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
isGlobal: isGlobalPolicyEffected(exception?.tags),
|
||||
});
|
||||
|
||||
const isEditMode = useMemo(() => !!exception?.item_id, [exception?.item_id]);
|
||||
const [wasByPolicy, setWasByPolicy] = useState(!isGlobalPolicyEffected(exception?.tags));
|
||||
|
||||
const showAssignmentSection = useMemo(() => {
|
||||
return (
|
||||
isPlatinumPlus ||
|
||||
(isEditMode &&
|
||||
(!selection.isGlobal || (wasByPolicy && selection.isGlobal && hasFormChanged)))
|
||||
);
|
||||
}, [isEditMode, selection.isGlobal, hasFormChanged, isPlatinumPlus, wasByPolicy]);
|
||||
|
||||
// set current policies if not previously selected
|
||||
useEffect(() => {
|
||||
if (selection.selected.length === 0 && exception?.tags) {
|
||||
|
@ -87,6 +103,13 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
}
|
||||
}, [exception?.tags, policies, selection.selected.length]);
|
||||
|
||||
// set initial state of `wasByPolicy` that checks if the initial state of the exception was by policy or not
|
||||
useEffect(() => {
|
||||
if (!hasFormChanged && exception?.tags) {
|
||||
setWasByPolicy(!isGlobalPolicyEffected(exception?.tags));
|
||||
}
|
||||
}, [exception?.tags, hasFormChanged]);
|
||||
|
||||
const osOptions: Array<EuiSuperSelectOption<OperatingSystem>> = useMemo(
|
||||
() => OPERATING_SYSTEMS.map((os) => ({ value: os, inputDisplay: OS_TITLES[os] })),
|
||||
[]
|
||||
|
@ -94,6 +117,15 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
|
||||
const handleOnBuilderChange = useCallback(
|
||||
(arg: OnChangeProps) => {
|
||||
if (
|
||||
(hasFormChanged === false && arg.exceptionItems[0] === undefined) ||
|
||||
(arg.exceptionItems[0] !== undefined &&
|
||||
exception !== undefined &&
|
||||
isEqual(exception?.entries, arg.exceptionItems[0].entries))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setHasFormChanged(true);
|
||||
dispatch({
|
||||
type: 'eventFiltersChangeForm',
|
||||
payload: {
|
||||
|
@ -114,12 +146,13 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
},
|
||||
});
|
||||
},
|
||||
[dispatch, exception?.name, exception?.comments, exception?.os_types, exception?.tags]
|
||||
[dispatch, exception, hasFormChanged]
|
||||
);
|
||||
|
||||
const handleOnChangeName = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!exception) return;
|
||||
setHasFormChanged(true);
|
||||
const name = e.target.value.toString().trim();
|
||||
dispatch({
|
||||
type: 'eventFiltersChangeForm',
|
||||
|
@ -135,6 +168,7 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
const handleOnChangeComment = useCallback(
|
||||
(value: string) => {
|
||||
if (!exception) return;
|
||||
setHasFormChanged(true);
|
||||
dispatch({
|
||||
type: 'eventFiltersChangeForm',
|
||||
payload: {
|
||||
|
@ -299,6 +333,7 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
}
|
||||
|
||||
if (!exception) return;
|
||||
setHasFormChanged(true);
|
||||
|
||||
dispatch({
|
||||
type: 'eventFiltersChangeForm',
|
||||
|
@ -321,12 +356,12 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
selected={selection.selected}
|
||||
options={policies}
|
||||
isGlobal={selection.isGlobal}
|
||||
isPlatinumPlus={true}
|
||||
isPlatinumPlus={isPlatinumPlus}
|
||||
onChange={handleOnChangeEffectScope}
|
||||
data-test-subj={'effectedPolicies-select'}
|
||||
/>
|
||||
),
|
||||
[policies, selection, handleOnChangeEffectScope]
|
||||
[policies, selection, isPlatinumPlus, handleOnChangeEffectScope]
|
||||
);
|
||||
|
||||
const commentsSection = useMemo(
|
||||
|
@ -356,18 +391,23 @@ export const EventFiltersForm: React.FC<EventFiltersFormProps> = memo(
|
|||
[commentsInputMemo]
|
||||
);
|
||||
|
||||
return !isIndexPatternLoading && exception && !arePoliciesLoading ? (
|
||||
if (isIndexPatternLoading || !exception || arePoliciesLoading) {
|
||||
return <Loader size="xl" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiForm component="div">
|
||||
{detailsSection}
|
||||
<EuiHorizontalRule />
|
||||
{criteriaSection}
|
||||
<EuiHorizontalRule />
|
||||
{policiesSection}
|
||||
{showAssignmentSection && (
|
||||
<>
|
||||
<EuiHorizontalRule /> {policiesSection}
|
||||
</>
|
||||
)}
|
||||
<EuiHorizontalRule />
|
||||
{commentsSection}
|
||||
</EuiForm>
|
||||
) : (
|
||||
<Loader size="xl" />
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue