-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solution][Endpoint] Artifacts event filter card on integrat…
…ion policy edit view (#121879) * fix typo refs elastic/security-team/issues/2031 * Add artifact event filters card to policy edit view on endpoint integration fixes elastic/security-team/issues/2031 * add tests fixes elastic/security-team/issues/2031 * fix typo refs /pull/111708 * use `eventFiltersListQueryHttpMock` instead review suggestion * add a test for verifying error toast review suggestion * fix tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
- Loading branch information
1 parent
9507d79
commit 34d7ca6
Showing
6 changed files
with
262 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
99 changes: 99 additions & 0 deletions
99
...ndpoint_package_custom_extension/components/fleet_integration_event_filters_card.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* 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 { waitFor, act } from '@testing-library/react'; | ||
import * as reactTestingLibrary from '@testing-library/react'; | ||
import { | ||
AppContextTestRender, | ||
createAppRootMockRenderer, | ||
} from '../../../../../../../common/mock/endpoint'; | ||
|
||
import { eventFiltersListQueryHttpMock } from '../../../../../event_filters/test_utils'; | ||
import { FleetIntegrationEventFiltersCard } from './fleet_integration_event_filters_card'; | ||
import { EndpointDocGenerator } from '../../../../../../../../common/endpoint/generate_data'; | ||
import { getPolicyEventFiltersPath } from '../../../../../../common/routing'; | ||
import { PolicyData } from '../../../../../../../../common/endpoint/types'; | ||
import { getFoundExceptionListItemSchemaMock } from '../../../../../../../../../lists/common/schemas/response/found_exception_list_item_schema.mock'; | ||
|
||
const endpointGenerator = new EndpointDocGenerator('seed'); | ||
|
||
describe('Fleet integration policy endpoint security event filters card', () => { | ||
let render: () => Promise<ReturnType<AppContextTestRender['render']>>; | ||
let renderResult: ReturnType<AppContextTestRender['render']>; | ||
let history: AppContextTestRender['history']; | ||
let mockedContext: AppContextTestRender; | ||
let mockedApi: ReturnType<typeof eventFiltersListQueryHttpMock>; | ||
let policy: PolicyData; | ||
|
||
beforeEach(() => { | ||
policy = endpointGenerator.generatePolicyPackagePolicy(); | ||
mockedContext = createAppRootMockRenderer(); | ||
mockedApi = eventFiltersListQueryHttpMock(mockedContext.coreStart.http); | ||
({ history } = mockedContext); | ||
render = async () => { | ||
await act(async () => { | ||
renderResult = mockedContext.render( | ||
<FleetIntegrationEventFiltersCard policyId={policy.id} /> | ||
); | ||
await waitFor(() => expect(mockedApi.responseProvider.eventFiltersList).toHaveBeenCalled()); | ||
}); | ||
return renderResult; | ||
}; | ||
|
||
history.push(getPolicyEventFiltersPath(policy.id)); | ||
}); | ||
|
||
afterEach(() => reactTestingLibrary.cleanup()); | ||
|
||
it('should call the API and render the card correctly', async () => { | ||
mockedApi.responseProvider.eventFiltersList.mockReturnValue( | ||
getFoundExceptionListItemSchemaMock(3) | ||
); | ||
|
||
await render(); | ||
expect(renderResult.getByTestId('eventFilters-fleet-integration-card')).toHaveTextContent( | ||
'Event filters3' | ||
); | ||
}); | ||
|
||
it('should show the card even when no event filters associated with the policy', async () => { | ||
mockedApi.responseProvider.eventFiltersList.mockReturnValue( | ||
getFoundExceptionListItemSchemaMock(0) | ||
); | ||
|
||
await render(); | ||
expect(renderResult.getByTestId('eventFilters-fleet-integration-card')).toBeTruthy(); | ||
}); | ||
|
||
it('should have the correct manage event filters link', async () => { | ||
mockedApi.responseProvider.eventFiltersList.mockReturnValue( | ||
getFoundExceptionListItemSchemaMock(1) | ||
); | ||
|
||
await render(); | ||
expect(renderResult.getByTestId('eventFilters-link-to-exceptions')).toHaveAttribute( | ||
'href', | ||
`/app/security/administration/policy/${policy.id}/eventFilters` | ||
); | ||
}); | ||
|
||
it('should show an error toast when API request fails', async () => { | ||
const error = new Error('Uh oh! API error!'); | ||
mockedApi.responseProvider.eventFiltersList.mockImplementation(() => { | ||
throw error; | ||
}); | ||
|
||
await render(); | ||
await waitFor(() => { | ||
expect(mockedContext.coreStart.notifications.toasts.addDanger).toHaveBeenCalledTimes(1); | ||
expect(mockedContext.coreStart.notifications.toasts.addDanger).toHaveBeenCalledWith( | ||
`There was an error trying to fetch event filters stats: "${error}"` | ||
); | ||
}); | ||
}); | ||
}); |
155 changes: 155 additions & 0 deletions
155
...ion/endpoint_package_custom_extension/components/fleet_integration_event_filters_card.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* 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 { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { FormattedMessage } from '@kbn/i18n-react'; | ||
import React, { memo, useEffect, useMemo, useRef, useState } from 'react'; | ||
import { INTEGRATIONS_PLUGIN_ID } from '../../../../../../../../../fleet/common'; | ||
import { pagePathGetters } from '../../../../../../../../../fleet/public'; | ||
import { | ||
GetExceptionSummaryResponse, | ||
PolicyDetailsRouteState, | ||
} from '../../../../../../../../common/endpoint/types'; | ||
import { useAppUrl, useHttp, useToasts } from '../../../../../../../common/lib/kibana'; | ||
import { getPolicyEventFiltersPath } from '../../../../../../common/routing'; | ||
import { parsePoliciesToKQL } from '../../../../../../common/utils'; | ||
import { ExceptionItemsSummary } from './exception_items_summary'; | ||
import { LinkWithIcon } from './link_with_icon'; | ||
import { StyledEuiFlexItem } from './styled_components'; | ||
import { EventFiltersHttpService } from '../../../../../event_filters/service'; | ||
|
||
export const FleetIntegrationEventFiltersCard = memo<{ | ||
policyId: string; | ||
}>(({ policyId }) => { | ||
const toasts = useToasts(); | ||
const http = useHttp(); | ||
const [stats, setStats] = useState<GetExceptionSummaryResponse | undefined>(); | ||
const isMounted = useRef<boolean>(); | ||
const { getAppUrl } = useAppUrl(); | ||
|
||
const eventFiltersApi = useMemo(() => new EventFiltersHttpService(http), [http]); | ||
const policyEventFiltersPath = getPolicyEventFiltersPath(policyId); | ||
|
||
const policyEventFiltersRouteState = useMemo<PolicyDetailsRouteState>(() => { | ||
const fleetPackageIntegrationCustomUrlPath = `#${ | ||
pagePathGetters.integration_policy_edit({ packagePolicyId: policyId })[1] | ||
}`; | ||
|
||
return { | ||
backLink: { | ||
label: i18n.translate( | ||
'xpack.securitySolution.endpoint.fleetCustomExtension.artifacts.backButtonLabel', | ||
{ | ||
defaultMessage: `Back to Fleet integration policy`, | ||
} | ||
), | ||
navigateTo: [ | ||
INTEGRATIONS_PLUGIN_ID, | ||
{ | ||
path: fleetPackageIntegrationCustomUrlPath, | ||
}, | ||
], | ||
href: getAppUrl({ | ||
appId: INTEGRATIONS_PLUGIN_ID, | ||
path: fleetPackageIntegrationCustomUrlPath, | ||
}), | ||
}, | ||
}; | ||
}, [getAppUrl, policyId]); | ||
|
||
const linkToEventFilters = useMemo( | ||
() => ( | ||
<LinkWithIcon | ||
href={getAppUrl({ | ||
path: policyEventFiltersPath, | ||
})} | ||
appPath={policyEventFiltersPath} | ||
appState={policyEventFiltersRouteState} | ||
data-test-subj="eventFilters-link-to-exceptions" | ||
size="m" | ||
> | ||
<FormattedMessage | ||
id="xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersManageLabel" | ||
defaultMessage="Manage event filters" | ||
/> | ||
</LinkWithIcon> | ||
), | ||
[getAppUrl, policyEventFiltersPath, policyEventFiltersRouteState] | ||
); | ||
|
||
useEffect(() => { | ||
isMounted.current = true; | ||
const fetchStats = async () => { | ||
try { | ||
const summary = await eventFiltersApi.getList({ | ||
perPage: 1, | ||
page: 1, | ||
filter: parsePoliciesToKQL([policyId, 'all']), | ||
}); | ||
if (isMounted.current) { | ||
setStats({ | ||
total: summary.total, | ||
windows: 0, | ||
linux: 0, | ||
macos: 0, | ||
}); | ||
} | ||
} catch (error) { | ||
if (isMounted.current) { | ||
toasts.addDanger( | ||
i18n.translate( | ||
'xpack.securitySolution.endpoint.fleetCustomExtension.eventFiltersSummary.error', | ||
{ | ||
defaultMessage: 'There was an error trying to fetch event filters stats: "{error}"', | ||
values: { error }, | ||
} | ||
) | ||
); | ||
} | ||
} | ||
}; | ||
fetchStats(); | ||
return () => { | ||
isMounted.current = false; | ||
}; | ||
}, [eventFiltersApi, policyId, toasts]); | ||
|
||
return ( | ||
<EuiPanel | ||
hasShadow={false} | ||
paddingSize="l" | ||
hasBorder | ||
data-test-subj="eventFilters-fleet-integration-card" | ||
> | ||
<EuiFlexGroup | ||
alignItems="baseline" | ||
justifyContent="flexStart" | ||
gutterSize="s" | ||
direction="row" | ||
responsive={false} | ||
> | ||
<EuiFlexItem grow={false}> | ||
<EuiText> | ||
<h5> | ||
<FormattedMessage | ||
id="xpack.securitySolution.endpoint.eventFilters.fleetIntegration.title" | ||
defaultMessage="Event filters" | ||
/> | ||
</h5> | ||
</EuiText> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<ExceptionItemsSummary stats={stats} isSmall={true} /> | ||
</EuiFlexItem> | ||
<StyledEuiFlexItem grow={1}>{linkToEventFilters}</StyledEuiFlexItem> | ||
</EuiFlexGroup> | ||
</EuiPanel> | ||
); | ||
}); | ||
|
||
FleetIntegrationEventFiltersCard.displayName = 'FleetIntegrationEventFiltersCard'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters